How to set multiple commands in one yaml file with Kubernetes?

196,600

Solution 1

command: ["/bin/sh","-c"]
args: ["command one; command two && command three"]

Explanation: The command ["/bin/sh", "-c"] says "run a shell, and execute the following instructions". The args are then passed as commands to the shell. In shell scripting a semicolon separates commands, and && conditionally runs the following command if the first succeed. In the above example, it always runs command one followed by command two, and only runs command three if command two succeeded.

Alternative: In many cases, some of the commands you want to run are probably setting up the final command to run. In this case, building your own Dockerfile is the way to go. Look at the RUN directive in particular.

Solution 2

My preference is to multiline the args, this is simplest and easiest to read. Also, the script can be changed without affecting the image, just need to restart the pod. For example, for a mysql dump, the container spec could be something like this:

containers:
  - name: mysqldump
    image: mysql
    command: ["/bin/sh", "-c"]
    args:
      - echo starting;
        ls -la /backups;
        mysqldump --host=... -r /backups/file.sql db_name;
        ls -la /backups;
        echo done;
    volumeMounts:
      - ...

The reason this works is that yaml actually concatenates all the lines after the "-" into one, and sh runs one long string "echo starting; ls... ; echo done;".

Solution 3

If you're willing to use a Volume and a ConfigMap, you can mount ConfigMap data as a script, and then run that script:

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-configmap
data:
  entrypoint.sh: |-
    #!/bin/bash
    echo "Do this"

    echo "Do that"
---
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-container
    image: "ubuntu:14.04"
    command:
    - /bin/entrypoint.sh
    volumeMounts:
    - name: configmap-volume
      mountPath: /bin/entrypoint.sh
      readOnly: true
      subPath: entrypoint.sh
  volumes:
  - name: configmap-volume
    configMap:
      defaultMode: 0700
      name: my-configmap

This cleans up your pod spec a little and allows for more complex scripting.

$ kubectl logs my-pod
Do this
Do that

Solution 4

If you want to avoid concatenating all commands into a single command with ; or && you can also get true multi-line scripts using a heredoc:

command: 
 - sh
 - "-c"
 - |
   /bin/bash <<'EOF'

   # Normal script content possible here
   echo "Hello world"
   ls -l
   exit 123

   EOF

This is handy for running existing bash scripts, but has the downside of requiring both an inner and an outer shell instance for setting up the heredoc.

Solution 5

I am not sure if the question is still active but due to the fact that I did not find the solution in the above answers I decided to write it down.

I use the following approach:

readinessProbe:
  exec:
    command:
    - sh
    - -c
    - |
      command1
      command2 && command3

I know my example is related to readinessProbe, livenessProbe, etc. but suspect the same case is for the container commands. This provides flexibility as it mirrors a standard script writing in Bash.

Share:
196,600

Related videos on Youtube

scho
Author by

scho

Updated on August 05, 2022

Comments

  • scho
    scho almost 2 years

    In this official document, it can run command in a yaml config file:

    https://kubernetes.io/docs/tasks/configure-pod-container/

    apiVersion: v1
    kind: Pod
    metadata:
      name: hello-world
    spec:  # specification of the pod’s contents
      restartPolicy: Never
      containers:
      - name: hello
        image: "ubuntu:14.04"
        env:
        - name: MESSAGE
          value: "hello world"
        command: ["/bin/sh","-c"]
        args: ["/bin/echo \"${MESSAGE}\""]
    

    If I want to run more than one command, how to do?

  • Michael Hausenblas
    Michael Hausenblas over 8 years
    Yes, very valid, however, I think there are also good use cases to extend command as it overrides the Dockerfile's Entrypoint ;)
  • Oliver
    Oliver about 6 years
    Very cool, but I think it is simpler to have the script inline, just use multiline syntax. I show this in a separate answer.
  • aclowkay
    aclowkay almost 6 years
    Any idea on how to do this with container lifecycle? It has no args
  • Tim Allclair
    Tim Allclair almost 6 years
    @aclokay you can just specify the arguments as additional command strings. The separation between command & args in the Container is just to make overriding the arguments easier. They are functionally equivalent.
  • sekrett
    sekrett almost 6 years
    Nice, but when you request an edit with kubectl, it will be in one line again. :)
  • aclowkay
    aclowkay over 5 years
    @sekrett oh no ! :(
  • kellyfj
    kellyfj over 5 years
    This worked quite nicely - the key is the semicolon on each line. This is a particularly good solution when the commands are many and would be multiline with the solution above. Makes git diff a breeze
  • Jingpeng Wu
    Jingpeng Wu over 5 years
    This is what I was looking for. using the environment variable as arguments with this solution works nicely.
  • L3K0V
    L3K0V almost 5 years
    What about when I need to pass double quotes. For example imagine this command: printf '%s @%s\n' "$(echo 'user')" "$(echo 'host')"
  • nelsonspbr
    nelsonspbr almost 5 years
    +1 Beautiful, plus multi-line commands work perfectly: command: ['/bin/bash', '-c'] args: - exec &> /path/to/redirected/program.output; ` python /program.py` ` --key1=val1` ` --key2=val2` ` --key3=val3`
  • Abdul
    Abdul over 4 years
    what -c does here?
  • Tim Allclair
    Tim Allclair over 4 years
    @Abdul it means run the script provided as an argument, rather than starting an interactive shell or loading the script from a file.
  • Yogi Ghorecha
    Yogi Ghorecha over 4 years
    if you keep & instead of && that means, it's not compulsory that the first command must be succeed.if first command is not succeed then it will go for the next, if you use & like this: args: ["command one; command two & command three"]
  • GACy20
    GACy20 over 3 years
    This makes no sense to me... Isn't -c an argument? Then why the heck would you put it inside command and not args? Then why not put everything inside command since that means that command does not have to contain only the executable but it can also add arguments... why bother separating the two? Why wouldn't command: ["/bin/sh", "-c", "command one; command two && command three""] work exactly like your example? This would make sense if you had to write command: "/bin/sh" and then args: ["-c", "..."]
  • WurmD
    WurmD almost 3 years
    But then if you go to edit the deployment yaml, it will be in one line, unreadable again
  • piscesgeek
    piscesgeek almost 3 years
    Editing a live resource with kubectl edit and the likes is an anti-pattern and a bad idea anyway. Your running configuration should always match what's written on your manifests. That's what declarative configuration and version control systems are for.
  • prince yadav
    prince yadav almost 3 years
    how we can setting up the heredoc?
  • prince yadav
    prince yadav almost 3 years
    by doing that getting the while running $SHELL output: \bin\ash
  • lauksas
    lauksas over 2 years
    way cleaner and it works better, thanks for the idea.
  • orkenstein
    orkenstein over 2 years
    This is the most flexible solution
  • aandis
    aandis over 2 years
    Why are command and args of list type instead of string.
  • MEMark
    MEMark over 2 years
    @YogiGhorecha In sh/bash/zsh a single & means to run that command in the background. You are probably thinking of ;
  • Krishna ps
    Krishna ps over 2 years
    thanks for the example
  • Krishna ps
    Krishna ps over 2 years
    thanks for the snippet.