-
-
Save jferris/1aba7433f5318715bda66b98c1d953f0 to your computer and use it in GitHub Desktop.
apiVersion: v1 | |
kind: ConfigMap | |
metadata: | |
name: example | |
namespace: default | |
data: | |
APPLICATION_HOST: example.com | |
LANG: en_US.UTF-8 | |
PIDFILE: /tmp/server.pid | |
PORT: "3000" | |
RACK_ENV: production | |
RAILS_ENV: production | |
RAILS_LOG_TO_STDOUT: "true" | |
RAILS_SERVE_STATIC_FILES: "true" |
apiVersion: batch/v1 | |
kind: Job | |
metadata: | |
generateName: db-migrate- | |
labels: | |
app.kubernetes.io/name: example | |
namespace: default | |
spec: | |
template: | |
metadata: | |
labels: | |
app.kubernetes.io/name: example | |
spec: | |
containers: | |
- command: | |
- rails | |
- db:migrate | |
envFrom: | |
- configMapRef: | |
name: env | |
- secretRef: | |
name: env | |
image: docker.io/mycompany/myapplication:abcd123 | |
imagePullPolicy: IfNotPresent | |
name: main | |
restartPolicy: Never |
apiVersion: apps/v1 | |
kind: Deployment | |
metadata: | |
labels: | |
app.kubernetes.io/name: example | |
process: web | |
name: example-web | |
namespace: default | |
spec: | |
selector: | |
matchLabels: | |
app.kubernetes.io/name: example | |
process: web | |
template: | |
metadata: | |
labels: | |
app.kubernetes.io/name: example | |
process: web | |
spec: | |
containers: | |
- env: | |
- name: PORT | |
value: "3000" | |
envFrom: | |
- configMapRef: | |
name: example | |
- secretRef: | |
name: example | |
image: docker.io/mycompany/myapplication:abcd123 | |
imagePullPolicy: IfNotPresent | |
name: main | |
ports: | |
- containerPort: 3000 | |
name: http | |
protocol: TCP | |
readinessProbe: | |
httpGet: | |
httpHeaders: | |
- name: Host | |
value: example.com | |
path: /robots.txt | |
port: 3000 | |
initialDelaySeconds: 10 | |
periodSeconds: 10 | |
timeoutSeconds: 2 | |
initContainers: | |
- command: | |
- rake | |
- db:abort_if_pending_migrations | |
envFrom: | |
- configMapRef: | |
name: example | |
- secretRef: | |
name: example | |
image: docker.io/mycompany/myapplication:abcd123 | |
imagePullPolicy: IfNotPresent | |
name: migrations |
apiVersion: networking.k8s.io/v1beta1 | |
kind: Ingress | |
metadata: | |
labels: | |
app.kubernetes.io/name: example | |
name: example | |
namespace: default | |
spec: | |
rules: | |
- host: example.com | |
http: | |
paths: | |
- backend: | |
serviceName: example-web | |
servicePort: 3000 | |
tls: | |
- hosts: | |
- example.com | |
secretName: example-tls |
apiVersion: v1 | |
kind: Service | |
metadata: | |
labels: | |
app.kubernetes.io/name: example | |
process: web | |
name: example-web | |
namespace: default | |
spec: | |
ports: | |
- name: http | |
port: 3000 | |
protocol: TCP | |
targetPort: http | |
selector: | |
app.kubernetes.io/name: example | |
process: web | |
type: ClusterIP |
@jferris thanks for your reply and suggestions.
In the meantime I found why generateName
makes sense - jobs are immutable so you cannot patch
them to redeploy.
so if you use a sidecar container in a job you'll need to manually kill it in the main container once the job is complete.
that's not trivial on its own but this solution worked for me: https://stackoverflow.com/a/64650086
To improve the speed of this, create a route specifically for the readinessProbe or even better, use a startupProbe (I am using the health_check gem). That route should, among other things, return 500 if migrations are pending. Then you can get rid of the initContainer. What will then happen is the app pods will start concurrently with the migration job and sit there returning 500's to k8s until the migrations finish, at which point they will return 200 and k8s will mark them as ready for traffic. This will cut deployment time in more than half compared to initContainers.
You can use
name
orgenerateName
, but if you usename
your CI process will need to delete the previous job first or migrations won't run.Kubernetes itself doesn't have any special knowledge of essential containers or sidecars so if you use a sidecar container in a job you'll need to manually kill it in the main container once the job is complete.
If your
initContainer
needs a sidecar to connect to the database, that approach might be difficult. A few ideas you could try:initContainer
with a readiness probe so the sidecar is running by the time you need to connect. If you do this, you may need to configure it to restart the container if the probe fails so that Rails will cache the latest database definitions.