Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Init dockerization and kube hosting #617

Merged
merged 1 commit into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 81 additions & 21 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Deploy production
name: Deploy production on Kube

on:
workflow_dispatch: ~
Expand All @@ -14,23 +14,25 @@ concurrency:

jobs:

deploy-production:
name: '🚧 Build & deploy 🚀'
build-production:
name: '🚧 Build 🚀'
runs-on: ubuntu-latest
timeout-minutes: 10

environment:
name: production
url: ${{ vars.WEBSITE_URL }}

permissions:
contents: read
packages: write

steps:
- name: 'Checkout'
uses: actions/checkout@v4

- name: 'Configure deployer SSH key'
uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.SSH_DEPLOY_KEY_PRODUCTION }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

# https://github.com/actions/setup-node
- name: 'Setup node'
Expand Down Expand Up @@ -86,25 +88,83 @@ jobs:
- name: 'Build static site'
run: |
bin/console stenope:build --no-interaction -vv --ansi --ignore-content-not-found
bin/console app:generate-redirections --target=site --no-interaction -vv --ansi > build/redirections-site.conf
bin/console app:generate-redirections --target=blog --no-interaction -vv --ansi > build/redirections-blog.conf
env:
APP_ENV: prod
ROUTER_DEFAULT_URI: ${{ vars.WEBSITE_URL }}
INCLUDE_SAMPLES: 0
SHOW_UNPUBLISHED_ARTICLES: 0
MATOMO_ID: ${{ vars.MATOMO_ID }}

- name: '🚀 Deploy'
- name: 'Login to GitHub Container Registry'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: elao-tools
password: ${{ secrets.GITHUB_TOKEN }}

- name: 'Build and push the Docker image for website prod'
uses: docker/build-push-action@v5
with:
context: .
file: kubernetes/Dockerfile
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
tags: |
ghcr.io/elao/website:latest

deploy-helm:
runs-on: ubuntu-22.04
timeout-minutes: 5
needs: [build-production]

steps:
- name: 'Set the Kubernetes context'
uses: azure/k8s-set-context@v4
with:
method: kubeconfig
kubeconfig: $${{ secrets.KUBERNETES_KUBECONFIG }}
context: kubernetes-admin@elao_argon-website-prod

- name: Checkout source code
uses: actions/checkout@v4

- name: 'Render charts website'
uses: azure/k8s-bake@v2.4
id: bake_website
with:
renderEngine: helm
helmChart: kubernetes/charts/website
helm-version: latest
silent: false

- name: 'Deploy to the Kubernetes cluster'
uses: azure/k8s-deploy@v5
with:
action: deploy
force: true
namespace: website-prod
manifests: ${{ steps.bake_website.outputs.manifestsBundle }}
pull-images: false

deploy-image:
runs-on: ubuntu-22.04
timeout-minutes: 5
needs: [deploy-helm]

steps:
- name: 'Set the Kubernetes context'
uses: azure/k8s-set-context@v4
with:
method: kubeconfig
kubeconfig: $${{ secrets.KUBERNETES_KUBECONFIG }}
context: kubernetes-admin@elao_argon-website-prod

- uses: azure/setup-kubectl@v4
name: Setup kubectl
with:
version: v1.28.2

- name: 'Rollout deployment website'
run: |
rsync build/ ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:${{ vars.DEPLOY_PATH }} \
--human-readable \
--compress \
--archive \
--delete \
--rsh "ssh -o StrictHostKeyChecking=no" \
--itemize-changes \
;

- name: 'Reload Nginx config'
run: ssh ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} 'sudo /bin/systemctl reload nginx'
kubectl rollout restart deployment/website --namespace website-prod
54 changes: 54 additions & 0 deletions kubernetes/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
ARG DEBIAN=bookworm

FROM debian:${DEBIAN}-slim as website

ARG DEBIAN
ARG NGINX_VERSION=1.26
ARG USER_ID=1000
ARG GROUP_ID=1000

RUN apt-get update \
&& apt-get install -y --no-install-recommends \
apt-transport-https \
ca-certificates \
curl \
gnupg2 \
sudo

# User
RUN addgroup --gid ${GROUP_ID} app \
&& adduser --home /home/app --shell /bin/bash --uid ${USER_ID} --gecos app --ingroup app --disabled-password app \
&& echo "app ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/app \
# App dir
&& mkdir --parents /srv && chown app:app /srv

#########
# Nginx #
#########
RUN \
echo "deb http://nginx.org/packages/debian/ ${DEBIAN} nginx" > /etc/apt/sources.list.d/nginx.list \
&& curl -sSL http://nginx.org/keys/nginx_signing.key \
| apt-key add - \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
nginx=${NGINX_VERSION}.* \
&& chown -R $USER_ID:0 /etc/nginx /var/log/nginx \
&& chmod -R g+w /etc/nginx

#################
# Configuration #
#################
COPY --chown=app:app kubernetes/app.conf /etc/nginx/app.conf
COPY --chown=app:app build/ /srv/app/website/

#########
# Clean #
#########
RUN \
rm -rf \
/var/lib/apt/lists/* \
/var/cache/debconf/*-old \
/var/lib/dpkg/*-old \
&& truncate -s 0 /var/log/*.log

CMD ["nginx", "-c", "/etc/nginx/app.conf"]
83 changes: 83 additions & 0 deletions kubernetes/app.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
include /etc/nginx/modules-enabled/*.conf;
worker_processes 1;

error_log stderr "warn";
pid /etc/nginx/nginx.pid;

events {
worker_connections 1024;
multi_accept on;
use epoll;
}

daemon off;

http {
include /etc/nginx/mime.types;

proxy_temp_path /tmp/proxy_temp;
client_body_temp_path /tmp/client_temp;
fastcgi_temp_path /tmp/fastcgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
scgi_temp_path /tmp/scgi_temp;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

log_format json escape=json '{'
'"remote_addr": "$remote_addr",'
'"remote_user": "$remote_user",'
'"time_local": "$time_local",'
'"request": "$request",'
'"request_length": $request_length,'
'"status": $status,'
'"body_bytes_sent": $body_bytes_sent,'
'"http_host": "$http_host",'
'"http_referer": "$http_referer",'
'"http_user_agent": "$http_user_agent",'
'"http_x_forwarded_for": "$http_x_forwarded_for",'
'"request_time": $request_time,'
'"upstream_connect_time": "$upstream_connect_time",'
'"upstream_header_time": "$upstream_header_time",'
'"upstream_response_time": "$upstream_response_time"'
'}';

access_log /dev/stdout json;

server {
listen 8080;
server_name www.elao.com;
root /srv/app/website;
absolute_redirect off;
gzip on;
gzip_disable msie6;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_types text/css text/javascript text/xml text/plain application/javascript application/x-javascript application/json application/xml application/rss+xml font/truetype application/x-font-ttf font/opentype application/vnd.ms-fontobject image/svg+xml;
# https://scotthelme.co.uk/hardening-your-http-response-headers/
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy strict-origin-when-cross-origin;
add_header Feature-Policy 'geolocation \'self\';fullscreen \'self\';microphone \'none\';camera \'none\';autoplay \'none\';payment \'none\';speaker \'none\'';
location ~* ^.+\.(?:css|cur|js|jpe?g|gif|htc|ico|png|xml|otf|ttf|eot|woff|woff2|svg|webp)$ {
expires 60d;
add_header Cache-Control public;
}
location / {
try_files $uri $uri/index.html =404;
}
location ~ \.html {
internal;
}
error_page 404 /404.html;
}

server {
listen 8080;
server_name blog.elao.com;
}
}
3 changes: 3 additions & 0 deletions kubernetes/charts/website/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
apiVersion: v2
name: website
version: 1.0.0
47 changes: 47 additions & 0 deletions kubernetes/charts/website/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "name" -}}
{{- .Chart.Name | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "fullname" -}}
{{- $name := .Chart.Name -}}
{{- if contains $name .Chart.Name -}}
{{- .Chart.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Chart.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Common labels
*/}}
{{- define "labels" -}}
helm.sh/chart: {{ include "chart" . }}
{{ include "selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "selectorLabels" -}}
app.kubernetes.io/name: {{ include "name" . }}
app.kubernetes.io/instance: {{ .Chart.Name }}
{{- end }}
38 changes: 38 additions & 0 deletions kubernetes/charts/website/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---

apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "fullname" . }}
labels:
{{- include "labels" . | nindent 4 }}
spec:
replicas: 1
selector:
matchLabels:
{{- include "selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "labels" . | nindent 8 }}
annotations: {}
spec:
imagePullSecrets:
- name: dockerconfig
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
containers:
- name: website
image: ghcr.io/elao/website:latest
resources:
requests:
cpu: 100m
memory: 512Mi
securityContext:
allowPrivilegeEscalation: false
imagePullPolicy: Always
ports:
- containerPort: 8080
protocol: TCP
Loading
Loading