Copying files to a local/host directory - kubernetes

I am trying to copy files from a container to a local/host directory. Running my experiments on minikube. Tried starting minikube with a mount as: minikube mount /tmp/export:/data/export and it still does not work.
I have a single pod, that upon startup runs a simple script:
timeout --signal=SIGINT 10s clinic bubbleprof -- node index.js >> /tmp/clinic.output.log && \
cp -R `grep "." /tmp/clinic.output.log | tail -1 | grep -oE '[^ ]+$'`* /data/export/ && \
echo "Finished copying clinic run generated files"
Once my script finishes its run, the container dies. This happens because bash is the process with PID 1. I don't mind this. My problem is that /tmp/export is empty, after the files should have been copied out.
My pod yaml:
apiVersion: v1
kind: Pod
metadata:
name: clinic-testapp
spec:
containers:
- name: clinic-testapp
image: username/container-image:0.0.11
ports:
- containerPort: 80
volumeMounts:
- name: clinic-storage
mountPath: /data/export
volumes:
- name: clinic-storage
hostPath:
path: /tmp/export
Am I doing something wrong? Please advise.

Related

kubernetes how to make a folder on my host as a Persistent volume

I am running a kuberneted cluster using minikube.
I want to mount a folder from my PC into the minikube.
How can i do that.
I see there is hostPath, but that used the node inside minikube
Just like in docker-compose we can mount a host folder into the container, is there any such provision
#Santhosh If i understand your question correctly, do you want to mount a path from within a container to a PV of type hostpath on your host? Have you tried this :-
apiVersion: v1
kind: Pod
metadata:
name: pv-recycler
namespace: default
spec:
restartPolicy: Never
volumes:
- name: vol
hostPath:
path: /any/path/it/will/be/replaced
containers:
- name: pv-recycler
image: "k8s.gcr.io/busybox"
command: ["/bin/sh", "-c", "test -e /scrub && rm -rf /scrub/..?* /scrub/.[!.]* /scrub/* && test -z \"$(ls -A /scrub)\" || exit 1"]
volumeMounts:
- name: vol
mountPath: /scrub

Restart a Kubernetes Job or Pod with a different command

I'm looking for a way to quickly run/restart a Job/Pod from the command line and override the command to be executed in the created container.
For context, I have a Kubernetes Job that gets executed as a part of our deploy process. Sometimes that Job crashes and I need to run certain commands inside the container the Job creates to debug and fix the problem (subsequent Jobs then succeed).
The way I have done this so far is:
Copy the YAML of the Job, save into a file
Clean up the YAML (delete Kubernetes-managed fields)
Change the command: field to tail -f /dev/null (so that the container stays alive)
kubectl apply -f job.yaml && kubectl get all && kubectl exec -ti pod/foobar bash
Run commands inside the container
kubectl delete job/foobar when I am done
This is very tedious. I am looking for a way to do something like the following
kubectl restart job/foobar --command "tail -f /dev/null"
# or even better
kubectl run job/foobar --exec --interactive bash
I cannot use the run command to create a Pod:
kubectl run --image xxx -ti
because the Job I am trying to restart has certain volumeMounts and other configuration I need to reuse. So I would need something like kubectl run --from-config job/foobar.
Is there a way to achieve this or am I stuck with juggling the YAML definition file?
Edit: the Job YAML looks approx. like this:
apiVersion: batch/v1
kind: Job
metadata:
name: database-migrations
labels:
app: myapp
service: myapp-database-migrations
spec:
backoffLimit: 0
template:
metadata:
labels:
app: myapp
service: myapp-database-migrations
spec:
restartPolicy: Never
containers:
- name: migrations
image: registry.example.com/myapp:977b44c9
command:
- "bash"
- "-c"
- |
set -e -E
echo "Running database migrations..."
do-migration-stuff-here
echo "Migrations finished at $(date)"
imagePullPolicy: Always
volumeMounts:
- mountPath: /home/example/myapp/app/config/conf.yml
name: myapp-config-volume
subPath: conf.yml
- mountPath: /home/example/myapp/.env
name: myapp-config-volume
subPath: .env
volumes:
- name: myapp-config-volume
configMap:
name: myapp
imagePullSecrets:
- name: k8s-pull-project
The commands you suggested don't exist. Take a look at this reference where you can find all available commands.
Based on that documentation the task of the Job is to create one or more Pods and continue retrying execution them until the specified number of successfully terminated ones will be achieved. Then the Job tracks the successful completions. You cannot just update the Job because these fields are not updatable. To do what's you want you should delete current job and create one once again.
I recommend you to keep all your configurations in files. If you have a problem with configuring job commands, practice says that you should modify these settings in yaml and apply to the cluster - if your deployment crashes - by storing the configuration in files, you have a backup.
If you are interested how to improve this task, you can try those 2 examples describe below:
Firstly I've created several files:
example job (job.yaml):
apiVersion: batch/v1
kind: Job
metadata:
name: test1
spec:
template:
spec:
containers:
- name: test1
image: busybox
command: ["/bin/sh", "-c", "sleep 300"]
volumeMounts:
- name: foo
mountPath: "/script/foo"
volumes:
- name: foo
configMap:
name: my-conf
defaultMode: 0755
restartPolicy: OnFailure
patch-file.yaml:
spec:
template:
spec:
containers:
- name: test1
image: busybox
command: ["/bin/sh", "-c", "echo 'patching test' && sleep 500"]
and configmap.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: my-conf
data:
test: |
#!/bin/sh
echo "skrypt test"
If you want to automate this process you can use plugin
A plugin is a standalone executable file, whose name begins with kubectl-. To install a plugin, move its executable file to anywhere on your PATH.
There is no plugin installation or pre-loading required. Plugin executables receive the inherited environment from the kubectl binary. A plugin determines which command path it wishes to implement based on its name.
Here is the file that can replace your job
A plugin determines the command path that it will implement based on its filename.
kubectl-job:
#!/bin/bash
kubectl patch -f job.yaml -p "$(cat patch-job.yaml)" --dry-run=client -o yaml | kubectl replace --force -f - && kubectl wait --for=condition=ready pod -l job-name=test1 && kubectl exec -it $(kubectl get pod -l job-name=test1 --no-headers -o custom-columns=":metadata.name") -- /bin/sh
This command uses an additional file (patch-job.yaml, see this link) - within we can put our changes for job.
Then you should change the permissions of this file and move it:
sudo chmod +x .kubectl-job
sudo mv ./kubectl-job /usr/local/bin
It's all done. Right now you can use it.
$ kubectl job
job.batch "test1" deleted
job.batch/test1 replaced
pod/test1-bdxtm condition met
pod/test1-nh2pv condition met
/ #
As you can see Job has been replaced (deleted and created).
You can also use single-line command, here is the example:
kubectl get job test1 -o json | jq "del(.spec.selector)" | jq "del(.spec.template.metadata.labels)" | kubectl patch -f - --patch '{"spec": {"template": {"spec": {"containers": [{"name": "test1", "image": "busybox", "command": ["/bin/sh", "-c", "sleep 200"]}]}}}}' --dry-run=client -o yaml | kubectl replace --force -f -
With this command you can change your job entering parameters "by hand". Here is the output:
job.batch "test1" deleted
job.batch/test1 replaced
As you can see this solution works as well.

container fails after executing command: [wget url]

i'm trying to download gzip file from a remote location, after download is complete the container Status changes to Completed then CrashLoopBackOff. the image below shows results of kubectl log my-service and kubectl describe pod my-service displays CrashLoopBackOff restarting failed container.
so i want this wget command to get executed during container initialization so i can gunzip and have the files accessed in a mounted volume. but this fails at container initialization
containers:
- name: my-service
image: docker.source.co.za/azp/my-service:1.0.0-SNAPSHOT
imagePullPolicy: Always
command:
- wget
- http://www.source.co.za/download/attachments/627674073/refpolicies.tar.gz
volumeMounts:
- name: my-service
mountPath: /test/
volumes:
- name: my-service
emptyDir: {}
The container stops after the command is executed. Kubernetes expects the container to run forever.
You can configure as below to achieve the same
command: ["/bin/sh","-c"]
args: ["wget url && sleep infinity"]
sleep infinity makes the container run forever doing nothing.

Combining multiple Local-SSD on a node in Kubernetes (GKE)

The data required by my container is too large to fit on one local SSD. I also need to access the SSD's as one filesystem from my container. So I would need to attach multiple ones. How do I combine them (single partition, RAID0, etc) and make them accessible as one volume mount in my container?
This link shares how to mount an SSD https://cloud.google.com/kubernetes-engine/docs/how-to/persistent-volumes/local-ssd to a mount path. I am not sure how you would merge multiple.
edit
The question asks how one would "combine" multiple SSD devices, individually mounted, on a single node in GKE.
WARNING
this is experimental and not intended for production use without
knowing what you are doing and only tested on gke version 1.16.x.
The approach includes a daemonset using a configmap to use nsenter (with wait tricks) for host namespace and privileged access so you can manage the devices. Specifically for GKE Local SSDs, we can unmount those devices and then raid0 them. InitContainer for the dirty work as this type of task seems most apparent for something you'd need to mark complete, and to then kill privileged container access (or even the Pod). Here is how it is done.
The example assumes 16 SSDs, however, you'll want to adjust the hardcoded values as necessary. Also, ensure your OS image reqs, I use Ubuntu. Also make sure the version of GKE you use starts local-ssd's at sd[b]
ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: local-ssds-setup
namespace: search
data:
setup.sh: |
#!/bin/bash
# returns exit codes: 0 = found, 1 = not found
isMounted() { findmnt -rno SOURCE,TARGET "$1" >/dev/null;} #path or device
# existing disks & mounts
SSDS=(/dev/sdb /dev/sdc /dev/sdd /dev/sde /dev/sdf /dev/sdg /dev/sdh /dev/sdi /dev/sdj /dev/sdk /dev/sdl /dev/sdm /dev/sdn /dev/sdo /dev/sdp /dev/sdq)
# install mdadm utility
apt-get -y update && apt-get -y install mdadm --no-install-recommends
apt-get autoremove
# OPTIONAL: determine what to do with existing, I wipe it here
if [ -b "/dev/md0" ]
then
echo "raid array already created"
if isMounted "/dev/md0"; then
echo "already mounted - unmounting"
umount /dev/md0 &> /dev/null || echo "soft error - assumed device was mounted"
fi
mdadm --stop /dev/md0
mdadm --zero-superblock "${SSDS[#]}"
fi
# unmount disks from host filesystem
for i in {0..15}
do
umount "${SSDS[i]}" &> /dev/null || echo "${SSDS[i]} already unmounted"
done
if isMounted "/dev/sdb";
then
echo ""
echo "unmount failure - prevent raid0" 1>&2
exit 1
fi
# raid0 array
yes | mdadm --create /dev/md0 --force --level=0 --raid-devices=16 "${SSDS[#]}"
echo "raid array created"
# format
mkfs.ext4 -F /dev/md0
# mount, change /mnt/ssd-array to whatever
mkdir -p /mnt/ssd-array
mount /dev/md0 /mnt/ssd-array
chmod a+w /mnt/ssd-array
wait.sh: |
#!/bin/bash
while sudo fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 1; done
DeamonSet pod spec
spec:
hostPID: true
nodeSelector:
cloud.google.com/gke-local-ssd: "true"
volumes:
- name: setup-script
configMap:
name: local-ssds-setup
- name: host-mount
hostPath:
path: /tmp/setup
initContainers:
- name: local-ssds-init
image: marketplace.gcr.io/google/ubuntu1804
securityContext:
privileged: true
volumeMounts:
- name: setup-script
mountPath: /tmp
- name: host-mount
mountPath: /host
command:
- /bin/bash
- -c
- |
set -e
set -x
# Copy setup script to the host
cp /tmp/setup.sh /host
# Copy wait script to the host
cp /tmp/wait.sh /host
# Wait for updates to complete
/usr/bin/nsenter -m/proc/1/ns/mnt -- chmod u+x /tmp/setup/wait.sh
# Give execute priv to script
/usr/bin/nsenter -m/proc/1/ns/mnt -- chmod u+x /tmp/setup/setup.sh
# Wait for Node updates to complete
/usr/bin/nsenter -m/proc/1/ns/mnt /tmp/setup/wait.sh
# If the /tmp folder is mounted on the host then it can run the script
/usr/bin/nsenter -m/proc/1/ns/mnt /tmp/setup/setup.sh
containers:
- image: "gcr.io/google-containers/pause:2.0"
name: pause
For high performance use cases, use the Ephemeral storage on local SSDs GKE feature. All local SSDs will be configures as a (striped) raid0 array and mounted into the pod.
Quick summary:
Create the node pool or cluster with the option: --ephemeral-storage local-ssd-count=X
Schedule to nodes with cloud.google.com/gke-ephemeral-storage-local-ssd.
Add an emptyDir volume.
Mount it with volumeMounts.
Here's how I used it with a DaemonSet:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: myapp
labels:
app: myapp
spec:
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
nodeSelector:
cloud.google.com/gke-ephemeral-storage-local-ssd: "true"
volumes:
- name: localssd
emptyDir: {}
containers:
- name: myapp
image: <IMAGE>
volumeMounts:
- mountPath: /scratch
name: localssd
You can use DaemonSet yaml file to deploy the pod will run on startup, assuming already created a cluster with 2 local-SSD (this pod will be in charge of creating the Raid0 disk)
kind: DaemonSet
apiVersion: extensions/v1beta1
metadata:
name: ssd-startup-script
labels:
app: ssd-startup-script
spec:
template:
metadata:
labels:
app: ssd-startup-script
spec:
hostPID: true
containers:
- name: ssd-startup-script
image: gcr.io/google-containers/startup-script:v1
imagePullPolicy: Always
securityContext:
privileged: true
env:
- name: STARTUP_SCRIPT
value: |
#!/bin/bash
sudo curl -s https://get.docker.com/ | sh
echo Done
The pod that will have access to the disk array in the above example is “/mnt/disks/ssd-array”
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: test-container
image: ubuntu
volumeMounts:
- mountPath: /mnt/disks/ssd-array
name: ssd-array
args:
- sleep
- "1000"
nodeSelector:
cloud.google.com/gke-local-ssd: "true"
tolerations:
- key: "local-ssd"
operator: "Exists"
effect: "NoSchedule"
volumes:
- name: ssd-array
hostPath:
path: /mnt/disks/ssd-array
After deploying the test-pod, SSH to the pod from your cloud-shell or any instance.
Then run :
kubectl exec -it test-pod -- /bin/bash
After that you should be able to see the created file in the ssd-array disk.
cat test-file.txt

cp command in yaml file

i'm launching a glassfish pod on my Kubernetes cluster, and i'm trying to copy some .war files from a folder that's on my host, but the command cp always seems to fail.
my yaml file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: glassfish
spec:
# replicas: 2
selector:
matchLabels:
app: glassfish
strategy:
type: Recreate
template:
metadata:
labels:
app: glassfish
spec:
containers:
- image: glassfish:latest
name: glassfish
ports:
- containerPort: 8080
name: glassfishhttp
- containerPort: 4848
name: glassfishadmin
command: ["/bin/cp"]
args: #["/mnt/apps/*","/usr/local/glassfish4/glassfish/domains/domain1/autodeploy/"]
- /mnt/apps/
- /usr/local/glassfish4/glassfish/domains/domain1/autodeploy/
volumeMounts:
- name: glassfish-persistent-storage
mountPath: /mount
- name: app
mountPath: /mnt/apps
volumes:
- name: glassfish-persistent-storage
persistentVolumeClaim:
claimName: fish-mysql-pvc
- name: app
hostPath:
path: /mnt/nfs
type: Directory
I'm trying to use the following command in my container:
cp /mnt/apps/* /usr/local/glassfish4/glassfish/domains/domain1/autodeploy
What am I doing wrong?
So far i've tried to do it with the /, without it, using /*
When i use apps/ i see "item or directory not found", when i use apps/ i get "directory ommitted", i need only whats in the map, not the map itself so -r doesn't really help either.
Two things to note here:
If you want to copy a direcyory using cp, you have to provide the -a or -R flag to cp:
-R If source_file designates a directory, cp copies the directory and the entire subtree connected at
that point. If the source_file ends in a /, the contents of the directory are copied rather than
the directory itself. This option also causes symbolic links to be copied, rather than indirected
through, and for cp to create special files rather than copying them as normal files. Created
directories have the same mode as the corresponding source directory, unmodified by the process'
umask.
In -R mode, cp will continue copying even if errors are detected.
If you use /bin/cp as your entrypoint in the pod, then this command is not executed in a shell. The * in /path/to/* however is a shell feature.
initContainers do not have args, only `command.
To make this work, use /bin/sh as the command instead:
command:
- /bin/sh
- -c
- cp /mnt/apps/* /usr/local/glassfish4/glassfish/domains/domain1/autodeploy/
What am I doing wrong?
here is correct command to execute:
command: ["sh", "-c", "cp -r /mnt/apps/* /usr/local/glassfish4/glassfish/domains/domain1/autodeploy/ && asadmin start-domain --verbose"]
With your cp command you effectively overwrite legitimate required command to start everything. You can see original one (running ok without your cp command) if you inspect that container. Initially, container is started with:
...
"Cmd": [
"/bin/sh",
"-c",
"asadmin start-domain --verbose"
],
...
simply adding it after the copy command solves your issue.