I was trying to add a poststart hook for my pod using curl, say sending a message to my slack channel
in shell, the command looks like this
curl -d "text=Hi I am a bot that can post messages to any public channel." -d "channel=C1234567" -H "Authorization: Bearer xoxb-xxxxxxxxxxxxxxxx" -X POST https://slack.com/api/chat.postMessage
and in my pod definition, i tried sth like this
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: curlimages/curl
env:
- name: TOKEN
valueFrom:
configMapKeyRef:
name: my-config
key: token
command: ["sleep"]
args: ["3000"]
lifecycle:
postStart:
exec:
command:
- "sh"
- "-c"
- |
curl -d "text=Hi going to start." -d "channel=C1234567" -H "Authorization: Bearer $(TOKEN)" -X POST https://slack.com/api/chat.postMessage
Unlike the container->command, it has args parameter which i could pass multi line command with quote, but in lifecycle->poststart->exec->command it doesn't support args parameter
I also tried sth like but no luck
command: ["curl","-d","text=Hi going to start.",....]
but i never got my slack message
My question is, how can i pass long curl command with quote in lifecycle->poststart->exec->command?
it finally solved by replacing () with {}
to use a env variable in command, it should be ${TOKEN}
Related
A container defined inside a deployment has a livenessProbe set up: by definition, it calls a remote endpoint and checks, whether response contains useful information or an empty response (which should trigger the pod's restart).
The whole definition is as follows (I removed the further checks for better clarity of the markup):
apiVersion: apps/v1
kind: Deployment
metadata:
name: fc-backend-deployment
labels:
name: fc-backend-deployment
app: fc-test
spec:
replicas: 1
selector:
matchLabels:
name: fc-backend-pod
app: fc-test
template:
metadata:
name: fc-backend-pod
labels:
name: fc-backend-pod
app: fc-test
spec:
containers:
- name: fc-backend
image: localhost:5000/backend:1.3
ports:
- containerPort: 4044
env:
- name: NODE_ENV
value: "dev"
- name: REDIS_HOST
value: "redis"
livenessProbe:
exec:
command:
- curl -X GET $BACKEND_SERVICE_HOST:$BACKEND_SERVICE_PORT/api/v3/stats | head -c 30 > /app/out.log
initialDelaySeconds: 20
failureThreshold: 12
periodSeconds: 10
I also tried putting the command into an array:
command: ["sh", "-c", "curl -X GET $BACKEND_SERVICE_HOST:$BACKEND_SERVICE_PORT/api/v3/stats", "|", "head", "-c", "30", ">", "/app/out.log"]
and splitting into separate lines:
- /bin/bash
- -c
- curl
- -X
- GET
- $BACKEND_SERVICE_HOST:$BACKEND_SERVICE_PORT/api/v3/stats
- |
- head
- -c
- "30"
- >
- /app/out.log
and even like this:
command:
- |
curl -X GET $BACKEND_SERVICE_HOST:$BACKEND_SERVICE_PORT/api/v3/stats | head -c 30 > /app/out.log
All attempts were made with and without (/bin/ba)sh -c - with the same result.
But, as you're reading this, you already know that none of these worked.
I know it by exec'ing into running container and trying to find the /app/out.log file - it wasn't present any time I watched the directory contents. It looks like the probe gets never executed.
The command run inside running container works just fine: data gets fetched and written to the specified file.
What might be causing the probe not to get executed?
When using the exec type of probes, Kubernetes will not run a shell to process the command, it will just run the command directly. This means that you can only use a single command and that the | character is considered just another parameter of your curl.
To solve the problem, you need to use sh -c to exec shell code, something like the following:
livenessProbe:
exec:
command:
- sh
- -c
- >-
curl -X GET $BACKEND_SERVICE_HOST:$BACKEND_SERVICE_PORT/api/v3/stats |
head -c 30 > /app/out.log
I am deploying my application in kubernetes using helm chart with 2 sub-charts app and test.
I have the pod of app chart properly running.
But test pod will be running only if it can properly authenticate to app container.
That means, i have to generate an auth_token using a curl request to app service and then add that token as Environment variable AUTH_TOKEN for test container.
I tried different ways to achieve this:
Added an init-container generate-token for test pod, that will generate the token and will save it in a shared volume. And test container will have access to that volume. But the problem here is, the test container doesn't have a code to set env for the container by reading from the shared volume.
Added a sidecar-container sidecar-generate-token instead of an init-container for the same setup as mentioned above. Here also problem is, the test container doesn't have a code to set env for the container by reading from the shared volume. And also, the test pod got into a crashloopbackoff state. If you check the content of volume by getting into the container, there are multiple tokens in the volume file which are generated on each pod restart of crashloopbackoff.
Third plan was that an init-container generate-token should create a kubernetes secret in the cluster, after generating the auth_token. Then the main container test can set Environment variable from that secret. For that, the init container generate-token should have a kubectl setup in it first.
If i am proceeding with the third plan, How can i setup and use kubectl from init-container to generate secret in the cluster?
Is there any other alternative plan to achieve this goal?
EDIT:
This is the yaml part for the first option:
initContainers:
- name: generate-service-token
image: app.mycr.io/alpine-network-troubleshooting:dev-latest
command:
- /bin/sh
- -c
- |
BEARER_TOKEN=$(curl -k -X POST -H "Content-Type:application/json" --data '{"user":"dynizer","password":"xxxx"}' "https://app:50051/api/v2/login" | jq -r '.jwt')
SERVICE_TOKEN=$(curl -k -X GET -H 'Accept: application/json' -H "Authorization: Bearer ${BEARER_TOKEN}" "https://app:50051/api/v2/servicetoken/issue" | jq -r '.token')
echo $SERVICE_TOKEN
mkdir -p /vol
touch /vol/token.txt
echo $SERVICE_TOKEN >> /vol/token.txt
volumeMounts:
- mountPath: /vol
name: token-vol
containers:
- name: nginx-container
image: nginx
volumeMounts:
- name: token-vol
mountPath: /vol
volumes:
- name: token-vol
emptyDir: {}
Trying to answer your question:
But still the same problem of container not having the code to set env by reading from the shared volume, will be there.
Let's try to read this env from other container. Here Is what I have come up with.
First you need to know what command your container is running. In case of nginx that is /docker-entrypoint.sh nginx -g "daemon off;" (source code)
Then you use command field where you read the token value from file and use env to set it and run the actual applciation.
Example:
initContainers:
- name: generate-service-token
image: app.mycr.io/alpine-network-troubleshooting:dev-latest
command:
- /bin/sh
- -c
- |
BEARER_TOKEN=$(curl -k -X POST -H "Content-Type:application/json" --data '{"user":"dynizer","password":"xxxx"}' "https://app:50051/api/v2/login" | jq -r '.jwt')
SERVICE_TOKEN=$(curl -k -X GET -H 'Accept: application/json' -H "Authorization: Bearer ${BEARER_TOKEN}" "https://app:50051/api/v2/servicetoken/issue" | jq -r '.token')
echo $SERVICE_TOKEN
mkdir -p /vol
touch /vol/token.txt
echo $SERVICE_TOKEN >> /vol/token.txt
volumeMounts:
- mountPath: /vol
name: token-vol
containers:
- name: nginx-container
image: nginx
command:
- sh
- -c
- exec env SERVICE_TOKEN=$(cat /vol/token.txt) /docker-entrypoint.sh nginx -g "daemon off;"
volumeMounts:
- name: token-vol
mountPath: /vol
volumes:
- name: token-vol
emptyDir: {}
More general example:
command:
- sh
- -c
- exec env SERVICE_TOKEN=$(cat /vol/token.txt) <<any command>>
I am not sure if this is the best example, but I hope that at least it gives you an idea how you can approach this problem.
Whenever I need to register my EKS services and required routes with kong, I have to manually execute CURL method( post/get ) commands for same, Services and routes get register successfully, but my requirement is to build or automate above multiple configurations with KONG, some way like producing a YAML file for all service registrations and routes for KONG and then executing at once.
I explored all the sources, even KONG official documentation, but couldn't find any way which ease my requirement
###################### Adding Svc ##########################################
curl -k -i -X POST \
--url https://localhost:7001/services/ \
--data 'name=hello-world1' \
--data 'host=service-helloworld' \
--data 'port=80'
###################### Adding Route ##########################################
curl -k -i -X POST --url https://localhost:7001/services/hello-world/routes --data 'paths=/hello-world' --data 'methods[]=GET'
Some way to automate above CURL commands
If I understand you correctly those are some of the ways you are looking for:
Container Lifecycle Hooks
In your case you would want to use PostStart
This hook executes immediately after a container is created. However, there is no guarantee that the hook will execute before the container ENTRYPOINT. No parameters are passed to the handler.
Hook handler implementations
Containers can access a hook by implementing and registering a handler for that hook. There are two types of hook handlers that can be implemented for Containers:
Exec - Executes a specific command, such as pre-stop.sh, inside the cgroups and namespaces of the Container. Resources consumed by the command are counted against the Container.
HTTP - Executes an HTTP request against a specific endpoint on the Container.
Your pod might look like the following example:
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: lifecycle-demo-container
image: nginx
lifecycle:
postStart:
exec:
command:
- "sh"
- "-c"
- >
curl -k -i -X POST --url https://localhost:7001/services/ --data 'name=hello-world1' --data 'host=service-helloworld' --data 'port=80';
curl -k -i -X POST --url https://localhost:7001/services/hello-world/routes --data 'paths=/hello-world' --data 'methods[]=GET'
Init Containers
A Pod can have multiple containers running apps within it, but it can also have one or more init containers, which are run before the app containers are started.
Init containers are exactly like regular containers, except:
Init containers always run to completion.
Each init container must complete successfully before the next one starts.
And here is an example from docs:
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
Let's say I have a Pod with 2 containers: App and Database. I want to run a Pod that executes a command in App and then terminates.
I have set up my App container to run that command, and then it succesully runs and terminates which is great. But now my Database container is still running, so the Pod is not marked as complete.
How can I get the Pod to be marked as complete when the App container is completed?
You can make a call to the Kubernetes API server to accomplish this. Consider the following example:
---
apiVersion: v1
kind: Pod
metadata:
name: multi-container-completion
spec:
containers:
- name: long-running-process
image: fbgrecojr/office-hours:so-47848488
command: ["sleep", "1000"]
- name: short-running-process
image: fbgrecojr/office-hours:so-47848488
command: ["sleep", "1"]
lifecycle:
preStop:
exec:
command: ["/pre-stop.sh"]
pre-stop.sh
#!/bin/bash
curl \
-X DELETE \
-H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
--cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
https://kubernetes.default.svc.cluster.local/api/v1/namespaces/$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)/pods/$HOSTNAME
Dockerfile for fbgrecojr/office-hours:so-47848488
FROM centos:latest
COPY pre-stop.sh /
RUN chmod +x /pre-stop.sh
NOTE: I was not able to properly test this because preStop hooks do not seem to be working for my local Minikube setup. In case this issue is not localized to me, the corresponding issue can be tracked here.
I want to define a livenessProbe with an httpHeader whose value is secret.
This syntax is invalid:
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: X-Custom-Header
valueFrom:
secretKeyRef:
name: my-secret-key
value: secret
If I specify my-secret-key with value secret as an environment variable named MY_SECRET_KEY, the following could work:
livenessProbe:
exec:
command:
- curl
- --fail
- -H
- "X-Custom-Header: $MY_SECRET_KEY"
- 'http://localhost:8080/healthz'
Unfortunately it doesn't due to the way the quotations are being evaluated. If I type the command curl --fail -H "X-Custom-Header: $MY_SECRET_KEY" http://localhost:8080/healthz directly on the container, it works.
I've also tried many combinations of single quotes and escaping the double quotes.
Does anyone know of a workaround?
Here some examples with curl and wget:
exec:
command:
- /bin/sh
- -c
- "curl -H 'Authorization: Bearer $(AUTH_TOKEN)' 'http://example.com'"
exec:
command:
- /bin/sh
- -c
- "wget --spider --header \"Authorization: Bearer $AUTH_TOKEN\" http://some.api.com/spaces/${SPACE_ID}/entries"
One workaround I can think of is to create some bash script to run this health check, and put your secret data to the environment as usual.