La vita in ogni respiro

“Il fiore perfetto è una cosa rara. Se si trascorresse la vita a cercarne uno, non sarebbe una vita sprecata.”

Un meraviglioso scontro-confronto tra due paradigmi di esistenza, uno che si fonda nel suo dinamismo, fretta, programmazione, efficienza e l’altro che cerca il fiore perfetto in ogni bocciolo, consapevole che ogni bocciolo morirà eppure conscio che la sua esistenza sarà compiuta, breve ed unica. Una bellezza aleatoria, non governabile, non gestibile.
Io che sono sempre stato espressione del primo pensiero nei minimi dettagli sono sempre più attratto dal secondo e sorrido rispetto alle motivazioni dell’efficienza e della programmazione perchè puoi adottare qualsiasi metodologia ma il carburante del successo è sentirsi parte di un qualcosa che in fondo ami/curi/credi in qualche modo.

La bellezza del fiore nel momento in cui lo vedi. Man mano riesco a far mio questo concetto, spingendomi a pensare al “cambiamento” in maniera diversa perché in fondo tutto cambia, il fiore muore e sta morendo anche nel momento di massima bellezza. Io non sono quello di dieci anni fa ma sopratutto anche le persone intorno a me cambiano, in maniera più o meno percettibile.

I bambini ad esempio cambiano ad una velocità per me difficile da accettare : ancora vorrei fare le coccole alla mia piccolina, fare l’aeroplano con il cucchiaino, ma ormai la piccolina non c’è più e anche se mi manca devo capire che quello che è stato è compiuto ed è stato bellissimo e lavorare su una nuova bellezza che se pur diversa è meravigliosa. Riconoscerla.
Adesso posso parlarle delle mie paure ad esempio, delle sue, della bellezza di un tramonto.

Amarsi in maniera diversa ma sempre amarsi.
Io questa cosa non l’avevo mica capita sai ? E’ importante compiersi e crescere amando sempre ma in modo diverso. Non rimpiangere niente.
Nel passato non c’è niente che una lezione per vivere la bellezza dell’adesso.
Adesso sei felice ? Cosa c’è stato di bello oggi ? Cosa hai imparato ? Hai amato ? Sei cresciuto ? Cosa non farai domani ? Cosa lasci al passato ?

Mi esplode il cervello se penso di poter essere ogni cosa, una qualunque parte del tutto. Il non-pensiero salda il corpo con lo spirito e libera dalla paura della morte. Ah se ho avuto paura della morte io !


Cambio il modo di vedere: i germogli non sono “nel fiore della vita”, ma stanno tutti morendo. È proprio in questa idealizzazione estetica della morte che si può riconoscere la vita in ogni respiro, in ogni tazza di te e in ogni vita che togliamo: la via del guerriero”.

Buongiorno vita

A tutte le persone che amano qualcuno che non hanno mai visto, a tutte le persone che amano ma non sono ricambiate, o non ancora. Continuate ad amare, e siate felici solo di questo. Amare è desiderare la felicità dell’altro, e anche se non dovesse andare come volete voi, nessuno potrà togliervi il vostro amore, che è la cosa più preziosa e vera che avete. Siate felici di amare: magari un giorno andrà bene, ma se non dovesse succedere siate felici perché l’altra persona è felice. Non sarà difficile se amate davvero. E non perdete le speranze, perché avete già tutto ciò di cui avete bisogno. A tutte le persone che amano, me compreso

Lo splendore della disobbedienza

antigone

Prendi tua figlia, portala a Siracusa, siediti sui gradoni del teatro greco e insegnale lo splendore della disubbidienza. E’ rischioso ma è più rischioso non farlo mai.

Gabriele Romagnoli, dall’articolo “Cercasi Antigone per la Rivoluzione”, Vanity Fair del 5-06-2013, pag. 74 “Antigone”, disse sua sorella ”Ti uccideranno”. “In qualche modo dovrò pur morire”, disse Antigone sempre rivolgendole le spalle. “Dovrò passare molto più tempo da morta che da viva”. “Sei pazza”, le disse la sorella.” E mi fai paura. Ma giuro. Non racconterò a nessuno quello che stai per fare”. L’affermazione fece girare la sorella minore per guardare di nuovo la maggiore negli occhi. ” URLALO PURE CON TUTTA LA VOCE CHE HAI!”, strillò.” NON MI IMPORTA CIO’ CHE DICI. NON MI IMPORTA CIO’ CHE CREONTE DICE. NON MI IMPORTA CIO’ CHE DICE LA GENTE, NON MI IMPORTA DI NESSUNO.”

Quando mi chiedono il motivo per cui ho chiamato mia figlia primogenita Antea, evito di dire la verità per timore di apparire troppo intellettuale radical chic o di sembrare una snob, cosa che non sono. Preferisco rispondere con motivazioni più o meno vere, ma che non sono LA ragione principale. Dico che l’ho scelta perché è un nome raro e originale, ma allo stesso tempo semplice e dolce. Dico che l’etimologia richiama la parola greca che significa “fiore” (anthos). Dico che mio padre si chiama Antonio e i due nomi hanno la stessa radice. Dico che all’università avevo una compagna di studi con quel nome, che poi ho perso di vista, ed era bellissima e intelligentissima. Ma la verità è che mia figlia si chiama così perché circa 2500 anni fa un tragediografo di nome Sofocle ha reso immortale uno dei personaggi più affascinanti della mitologia greca: Antigone.

Dicevano i latini che nel nome c’è il nostro destino (“nomen est omen”), ma non auguro certo ad Antea il destino di Antigone. Figlia di Edipo, che si strappa gli occhi quando scopre di aver amato sua madre, Antigone si oppone al divieto imposto dallo zio Creonte di seppellire il corpo del fratello Polinice, che avrebbe dovuto giacere senza dignità e pietà umana, in pasto alle bestie e agli uccelli predatori, poiché si era opposto al fratello Eteocle, usurpatore del trono di Tebe. La ribellione di Antigone contro una legge ingiusta, che va contro la legge naturale di dare una degna sepoltura ai morti, le costerà la vita.

Nel nome di mia figlia, così come in quello di Antigone, c’è questa parola: “anti”, questo seme di ribellione e anticonformismo che spero germogli. Come scrive Romagnoli nel suo articolo, tutte le istituzioni, a cominciare dalla famiglia, insegnano ai bambini il valore dell’obbedienza. Antigone, invece, insegna il valore della disobbedienza. L’obbedienza viene premiata, la disobbedienza viene punita. Richiede coraggio, spirito di sacrificio e idealismo. Ma è la disobbedienza a tracciare il solco per l’avanzamento della storia sulla via della giustizia e non sulla volontà di qualsiasi governante.

Ecco perché ho chiamato mia figlia Antea. Volevo che il suo nome trasmettesse, per “osmosi”, proprio queste virtù elencate da Romagnoli: il coraggio, lo spirito di sacrificio, l’idealismo. Perché Antigone è anche Ipazia, che nel IV secolo d.C. viene uccisa per difendere il suo amore per la scienza. È Malala Yousafzai, che ha rischiato la vita per affermare il diritto all’istruzione delle bambine pakistane. È Eleonora Pimentel da Fonseca, è Rosa Parks, è Dolores Ibá

NGINX/PHP-FPM graceful shutdown and 502 errors

We have a PHP application running with Kubernetes in pods with two dedicated containers — NGINX и PHP-FPM.

The problem is that during downscaling clients get 502 errors. E.g. when a pod is stopping, its containers can not correctly close existing connections.

So, in this post, we will take a closer look at the pods’ termination process in general, and NGINX and PHP-FPM containers in particular.

Testing will be performed on the AWS Elastic Kubernetes Service by the Yandex.Tank utility.

Ingress resource will create an AWS Application Load Balancer with the AWS ALB Ingress Controller.

Для управления контейнерами на Kubernetes WorkerNodes испольузется Docker.

Pod Lifecycle — Termination of Pods

So, let’s take an overview of the pods’ stopping and termination process.

Basically, a pod is a set of processes running on a Kubernetes WorkerNode, which are stopped by standard IPC (Inter-Process Communicationsignals.

To give the pod the ability to finish all its operations, a container runtime at first ties softly stop it (graceful shutdown) by sending a SIGTERM signal to a PID 1 in each container of this pod (see docker stop). Also, a cluster starts counting a grace period before force kill this pod by sending a SIGKILL signal.

The SIGTERM can be overridden by using the STOPSIGNAL in an image used to spin up a container.

Thus, the whole flow of the pod’s deleting is (actually, the part below is a kinda copy of the official documentation):

  1. a user issues a kubectl delete pod or kubectl scale deployment command which triggers the flow and the cluster start countdown of the grace period with the default value set to the 30 second
  2. the API server of the cluster updates the pod’s status — from the Running state, it becomes the Terminating (see Container states). A kubelet on the WorkerNode where this pod is running, receives this status update and start the pod’s termination process:
  3. if a container(s) in the pod have a preStop hook – kubelet will run it. If the hook is still running the default 30 seconds on the grace period – another 2 seconds will be added. The grace period can be set with the terminationGracePeriodSeconds
  4. when a preStop hook is finished, a kubelet will send a notification to the Docker runtime to stop containers related to the pod. The Docker daemon will send the SIGTERM signal to a process with the PID 1 in each container. Containers will get the signal in random order.
  5. simultaneously with the beginning of the graceful shutdown — Kubernetes Control Plane (its kube-controller-manager) will remove the pod from the endpoints (see Kubernetes – Endpoints) and a corresponding Service will stop sending traffic to this pod
  6. after the grace period countdown is finished, a kubelet will start force shutdown – Docker will send the SIGKILL signal to all remaining processes in all containers of the pod which can not be ignored and those process will be immediately terminated without change to correctly finish their operations
  7. kubelet triggers deletion of the pod from the API server
  8. API server deletes a record about this pod from the etcd
Image for post

Actually, there are two issues:

  1. the NGINX and PHP-FPM perceives the SIGTERM signal as a force как “brutal murder” and will finish their processes immediately , и завершают работу немедленно, without concern about existing connections (see Controlling nginx and php-fpm(8) – Linux man page)
  2. the 2 and 3 steps — sending the SIGTERM and an endpoint deletion – are performed at the same time. Still, an Ingress Service will update its data about endpoints not momentarily and a pod can be killed before then an Ingress will stop sending traffic to it causing 502 error for clients as the pod can not accept new connections

E.g. if we have a connection to an NGINX server, the NGINX master process during the fast shutdown will just drop this connection and our client will receive the 502 error, see the Avoiding dropped connections in nginx containers with “STOPSIGNAL SIGQUIT”.

NGINX STOPSIGNAL and 502

Okay, now we got some understanding of how it’s going — let’s try to reproduce the first issue with NGINX.

The example below is taken from the post above and will be deployed to a Kubernetes cluster.

Prepare a Dockerfile:

FROM nginx

RUN echo 'server {\n\
listen 80 default_server;\n\
location / {\n\
proxy_pass http://httpbin.org/delay/10;\n\
}\n\
}' > /etc/nginx/conf.d/default.conf

CMD ["nginx", "-g", "daemon off;"]

Here NGINX will proxy_pass a request to the http://httpbin.org which will respond with a 10 seconds delay to emulate a PHP-backend.

Build an image and push it to a repository:

$ docker build -t setevoy/nginx-sigterm .
$ docker push setevoy/nginx-sigterm

Now, add a Deployment manifest to spin up 10 pods from this image.

Here is the full file with a Namespace, Service, and Ingress, in the following part of this post, will add only updated parts of the manifest:

---
apiVersion: v1
kind: Namespace
metadata:
name: test-namespace
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-deployment
namespace: test-namespace
labels:
app: test
spec:
replicas: 10
selector:
matchLabels:
app: test
template:
metadata:
labels:
app: test
spec:
containers:
- name: web
image: setevoy/nginx-sigterm
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 100Mi
readinessProbe:
tcpSocket:
port: 80
---
apiVersion: v1
kind: Service
metadata:
name: test-svc
namespace: test-namespace
spec:
type: NodePort
selector:
app: test
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress
namespace: test-namespace
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]'
spec:
rules:
- http:
paths:
- backend:
serviceName: test-svc
servicePort: 80

Deploy it:

$ kubectl apply -f test-deployment.yaml
namespace/test-namespace created
deployment.apps/test-deployment created
service/test-svc created
ingress.extensions/test-ingress created

Check the Ingress:

$ curl -I aadca942-testnamespace-tes-5874–698012771.us-east-2.elb.amazonaws.com
HTTP/1.1 200 OK

And we have 10 pods running:

$ kubectl -n test-namespace get pod
NAME READY STATUS RESTARTS AGE
test-deployment-ccb7ff8b6–2d6gn 1/1 Running 0 26s
test-deployment-ccb7ff8b6–4scxc 1/1 Running 0 35s
test-deployment-ccb7ff8b6–8b2cj 1/1 Running 0 35s
test-deployment-ccb7ff8b6-bvzgz 1/1 Running 0 35s
test-deployment-ccb7ff8b6-db6jj 1/1 Running 0 35s
test-deployment-ccb7ff8b6-h9zsm 1/1 Running 0 20s
test-deployment-ccb7ff8b6-n5rhz 1/1 Running 0 23s
test-deployment-ccb7ff8b6-smpjd 1/1 Running 0 23s
test-deployment-ccb7ff8b6-x5dc2 1/1 Running 0 35s
test-deployment-ccb7ff8b6-zlqxs 1/1 Running 0 25s

Prepare a load.yaml for the Yandex.Tank:

phantom:
address: aadca942-testnamespace-tes-5874-698012771.us-east-2.elb.amazonaws.com
header_http: "1.1"
headers:
- "[Host: aadca942-testnamespace-tes-5874-698012771.us-east-2.elb.amazonaws.com]"
uris:
- /
load_profile:
load_type: rps
schedule: const(100,30m)
ssl: false
console:
enabled: true
telegraf:
enabled: false
package: yandextank.plugins.Telegraf
config: monitoring.xml

Here, we will perform 1 request per second to pods behind our Ingress.

Run tests:

Image for post

All good so far.

Now, scale down the Deployment to only one pod:

$ kubectl -n test-namespace scale deploy test-deployment — replicas=1
deployment.apps/test-deployment scaled

Pods became Terminating:

$ kubectl -n test-namespace get pod
NAME READY STATUS RESTARTS AGE
test-deployment-647ddf455–67gv8 1/1 Terminating 0 4m15s
test-deployment-647ddf455–6wmcq 1/1 Terminating 0 4m15s
test-deployment-647ddf455-cjvj6 1/1 Terminating 0 4m15s
test-deployment-647ddf455-dh7pc 1/1 Terminating 0 4m15s
test-deployment-647ddf455-dvh7g 1/1 Terminating 0 4m15s
test-deployment-647ddf455-gpwc6 1/1 Terminating 0 4m15s
test-deployment-647ddf455-nbgkn 1/1 Terminating 0 4m15s
test-deployment-647ddf455-tm27p 1/1 Running 0 26m

And we got our 502 errors:

Image for post

Next, update the Dockerfile — add the STOPSIGNAL SIGQUIT:

FROM nginx

RUN echo 'server {\n\
listen 80 default_server;\n\
location / {\n\
proxy_pass http://httpbin.org/delay/10;\n\
}\n\
}' > /etc/nginx/conf.d/default.conf

STOPSIGNAL SIGQUIT

CMD ["nginx", "-g", "daemon off;"]

Build, push:

$ docker build -t setevoy/nginx-sigquit .
docker push setevoy/nginx-sigquit

Update the Deployment with the new image:

...
spec:
containers:
- name: web
image: setevoy/nginx-sigquit
ports:
- containerPort: 80
...

Redeploy, and check again.

Run tests:

Image for post

Scale down the deployment again:

$ kubectl -n test-namespace scale deploy test-deployment — replicas=1
deployment.apps/test-deployment scaled

And no errors this time:

Image for post

Great!

Traffic, preStop, and sleep

But still, if repeat tests few times we still can get some 502 errors:

Image for post

This time most likely we are facing the second issue — endpoints update is performed at the same time when the SIGTERM Is sent.

Let’s add a preStop hook with the sleep to give some time to update endpoints and our Ingress, so after the cluster will receive a request to stop a pod, a kubelet on a WorkerNode will wait for 5 seconds before sending the SIGTERM:

...
spec:
containers:
- name: web
image: setevoy/nginx-sigquit
ports:
- containerPort: 80
lifecycle:
preStop:
exec:
command: ["/bin/sleep","5"]
...

Repeat tests — and now everything is fine

Our PHP-FPM had no such issue as its image was initially built with the STOPSIGNAL SIGQUIT.

Other possible solutions

And of course, during debugging I’ve tried some other approaches to mitigate the issue.

See links at the end of this post and here I’ll describe them in short terms.

preStop and nginx -s quit

One of the solutions was to add a preStop hook which will send QUIT to NGINX:

lifecycle:
preStop:
exec:
command:
- /usr/sbin/nginx
- -s
- quit

Or:

...
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -SIGQUIT
- 1
....

But it didn’t help. Not sure why as the idea seems to be correct — instead of waiting for the TERM from Kubernetes/Docker – we gracefully stopping the NGINX master process by sending QUIT.

You can also run the strace utility check which signal is really received by the NGINX.

NGINX + PHP-FPM, supervisord, and stopsignal

Our application is running in two containers in one pod, but during the debugging, I’ve also tried to use a single container with both NGINX and PHP-FPM, for example, trafex/alpine-nginx-php7.

There I’ve tried to add to stopsignal to the supervisor.conf for both NGINX and PHP-FPM with the QUIT value, but this also didn’t help although the idea also seems to be correct.

Still, one can try this way.

PHP-FPM, and process_control_timeout

In the Graceful shutdown in Kubernetes is not always trivial and on the Stackoveflow in the Nginx / PHP FPM graceful stop (SIGQUIT): not so graceful question is a note that FPM’s master process is killed before its child and this can lead to the 502 as well.

Not our current case, but pay your attention to the process_control_timeout.

NGINX, HTTP, and keep-alive session

Also, it can be a good idea to use the [Connection: close] header – then the client will close its connection right after a request is finished and this can decrease 502 errors count.

But anyway they will persist if NGINX will get the SIGTERM during processing a request.

See the HTTP persistent connection.