Skip to content

Instantly share code, notes, and snippets.

@mkhmylife
Created May 3, 2019 18:31
Show Gist options
  • Save mkhmylife/03036f2e246cd75d4338457a1ea25fcd to your computer and use it in GitHub Desktop.
Save mkhmylife/03036f2e246cd75d4338457a1ea25fcd to your computer and use it in GitHub Desktop.
Laravel Kubernetes Deployment files
apiVersion: v1
kind: ConfigMap
metadata:
name: backend-config
data:
APP_DEBUG: "false"
APP_ENV: production
APP_KEY: changeme
APP_LOG_LEVEL: debug
APP_NAME: "Laravel K8s"
APP_URL: https://www.laravel.com
BROADCAST_DRIVER: pusher
CACHE_DRIVER: redis
QUEUE_DRIVER: redis
SESSION_DRIVER: redis
DB_CONNECTION: mysql
DB_DATABASE: backend
DB_HOST: changeme
DB_PASSWORD: beta1234
DB_PORT: "3306"
DB_USERNAME: beta
REDIS_HOST: redis
REDIS_PORT: "6379"
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: backend-cron
spec:
concurrencyPolicy: Replace
failedJobsHistoryLimit: 1
jobTemplate:
spec:
activeDeadlineSeconds: 120
backoffLimit: 1
template:
spec:
containers:
- args:
- /bin/bash
- -c
- php artisan schedule:run
envFrom:
- configMapRef:
name: backend-config
image: changeme
imagePullPolicy: Always
name: artisan-schedule
resources:
limits:
cpu: 200m
memory: 200M
requests:
cpu: 100m
memory: 100M
restartPolicy: Never
schedule: "*/1 * * * *"
startingDeadlineSeconds: 30
successfulJobsHistoryLimit: 1
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: backend
name: backend
spec:
minReadySeconds: 5
replicas: 3
revisionHistoryLimit: 1
selector:
matchLabels:
app: backend
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 50%
type: RollingUpdate
template:
metadata:
labels:
app: backend
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- backend
topologyKey: kubernetes.io/hostname
weight: 100
initContainers:
- args:
- /bin/bash
- -c
- (php artisan migrate || true) && (php artisan config:cache || true) && (php
artisan route:cache || true) && (cp -rp /var/www/html /codebase)
envFrom:
- configMapRef:
name: backend-config
image: changeme
imagePullPolicy: Always
name: artisan
volumeMounts:
- mountPath: /codebase
name: codebase
containers:
- name: app
envFrom:
- configMapRef:
name: backend-config
image: changeme
imagePullPolicy: Always
livenessProbe:
initialDelaySeconds: 10
periodSeconds: 15
tcpSocket:
port: 80
timeoutSeconds: 30
ports:
- containerPort: 80
readinessProbe:
initialDelaySeconds: 10
periodSeconds: 10
tcpSocket:
port: 80
resources:
limits:
cpu: 200m
memory: 400M
requests:
cpu: 100m
memory: 200M
volumeMounts:
- mountPath: /var/www
name: codebase
volumes:
- emptyDir: {}
name: codebase
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: horizon
name: horizon
spec:
replicas: 1
revisionHistoryLimit: 1
selector:
matchLabels:
app: horizon
template:
metadata:
labels:
app: horizon
spec:
containers:
- args:
- /bin/bash
- -c
- php artisan horizon
envFrom:
- configMapRef:
name: backend-config
image: changeme
imagePullPolicy: Always
name: horizon
resources:
limits:
cpu: 200m
memory: 1G
requests:
cpu: 100m
memory: 500M
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: 100m
labels:
app: backend
name: backend
spec:
rules:
- host: yourdomain.com
http:
paths:
- backend:
serviceName: backend
servicePort: 80
path: /
# tls:
# - hosts:
# - yourdomain.com
# secretName: yourdomain-com-tls
# ---
# apiVersion: certmanager.k8s.io/v1alpha1
# kind: Certificate
# metadata:
# name: yourdomain.com
# spec:
# acme:
# config:
# - dns01:
# provider: cf-dns
# domains:
# - yourdomain.com
# commonName: yourdomain.com
# dnsNames:
# - yourdomain.com
# issuerRef:
# kind: ClusterIssuer
# name: letsencrypt
# secretName: yourdomain-com-tls
apiVersion: v1
kind: Service
metadata:
labels:
app: backend
name: backend
spec:
ports:
- name: http
port: 80
protocol: TCP
selector:
app: backend
type: ClusterIP
@kolinko-ant
Copy link

Hi, thanks for the example with horizon. Do you know what is the proper way to terminate horizon in this case? If we want to deploy a new worker, docs says we just have to run horizon:terminate and it will be restarted by supervisor. In k8s case seems like we need to delete pod first by running horizon:terminate and then create a new pod with artisan:horizon. Is that right? How did you do that?

@erloncharlesbf
Copy link

@kolinko-ant I'm trying like this

apiVersion: apps/v1
kind: Deployment
metadata:
  name: horizon-worker
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: horizon-worker
    spec:
      containers:
        - name: laravel
          image: changeme
          command: ["/usr/local/bin/php", "artisan", "horizon"]
          lifecycle:
            preStop:
              exec:
                command: ["/usr/local/bin/php", "artisan", "horizon:terminate"]

@superbiche
Copy link

superbiche commented Apr 15, 2025

Use horizon:terminate --wait.
Also if you have 3 replicas use

strategy:
    type: Recreate

in order to make sure every old replica runs horizon:terminate --wait before starting the new ones. Otherwise, some old replicas will call horizon:terminate --wait while new replicas have just started. We had some large job batches (60k+) where some jobs stayed as Pending for hours before we found out this was one of our problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment