如何使用 Kubernetes 在一个 yaml 文件中设置多个命令?
How to set multiple commands in one yaml file with Kubernetes?
在这个官方文档中,可以运行在yaml配置文件中命令:
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}\""]
如果我想运行多个命令,怎么办?
command: ["/bin/sh","-c"]
args: ["command one; command two && command three"]
解释: command ["/bin/sh", "-c"]
表示 "run a shell, and execute the following instructions"。然后将 args 作为命令传递给 shell。在 shell 脚本中,分号分隔命令,并且 &&
有条件地 运行 如果第一个命令成功,则执行以下命令。在上面的例子中,它总是 运行s command one
后跟 command two
,只有 运行s command three
如果 command two
成功。
备选方案: 在许多情况下,您想要 运行 的某些命令可能会将最终命令设置为 运行。在这种情况下,特别是构建您自己的 Dockerfile is the way to go. Look at the RUN 指令。
如果您愿意使用 Volume 和 ConfigMap,您可以 mount ConfigMap data 作为脚本,然后 运行 该脚本:
---
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
这稍微清理了您的 pod 规范并允许编写更复杂的脚本。
$ kubectl logs my-pod
Do this
Do that
我更喜欢将参数多行化,这样最简单也最容易阅读。另外,可以在不影响镜像的情况下修改脚本,只需要重启pod即可。例如,对于 mysql 转储,容器规范可能是这样的:
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:
- ...
这样做的原因是 yaml 实际上将“-”之后的所有行连接成一个,而 sh 运行一个长字符串 "echo starting; ls... ; echo done;"。
如果您想避免使用 ;
或 &&
将所有命令连接成一个命令,您还可以使用 heredoc 获得真正的多行脚本:
command:
- sh
- "-c"
- |
/bin/bash <<'EOF'
# Normal script content possible here
echo "Hello world"
ls -l
exit 123
EOF
这对于 运行 现有的 bash 脚本很方便,但缺点是需要内部和外部 shell 实例来设置 heredoc。
只是为了提供另一种可能的选择,可以使用秘密,因为它们以卷的形式呈现给 pod:
秘密示例:
apiVersion: v1
kind: Secret
metadata:
name: secret-script
type: Opaque
data:
script_text: <<your script in b64>>
Yaml 提取:
....
containers:
- name: container-name
image: image-name
command: ["/bin/bash", "/your_script.sh"]
volumeMounts:
- name: vsecret-script
mountPath: /your_script.sh
subPath: script_text
....
volumes:
- name: vsecret-script
secret:
secretName: secret-script
我知道很多人会争辩说这不是必须使用秘密的目的,但它是一种选择。
恕我直言,最好的选择是使用 YAML 的原生 block scalars。具体在这种情况下,folded 样式块。
通过调用 sh -c
,您可以将参数作为命令传递给您的容器,但如果您想用换行符优雅地分隔它们,您需要使用 折叠样式块,这样 YAML 就会知道将换行符转换为空格,从而有效地连接命令。
一个完整的工作示例:
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
app: myapp
spec:
containers:
- name: busy
image: busybox:1.28
command: ["/bin/sh", "-c"]
args:
- >
command_1 &&
command_2 &&
...
command_n
Here is my successful run
apiVersion: v1
kind: Pod
metadata:
labels:
run: busybox
name: busybox
spec:
containers:
- command:
- /bin/sh
- -c
- |
echo "running below scripts"
i=0;
while true;
do
echo "$i: $(date)";
i=$((i+1));
sleep 1;
done
name: busybox
image: busybox
我不确定这个问题是否仍然有效,但由于我没有在上述答案中找到解决方案,我决定把它写下来。
我使用以下方法:
readinessProbe:
exec:
command:
- sh
- -c
- |
command1
command2 && command3
我知道我的示例与 readinessProbe、livenessProbe 等相关,但怀疑容器命令也是如此。这提供了灵活性,因为它反映了在 Bash.
中编写的标准脚本
这是运行多行命令的另一种方法。
apiVersion: batch/v1
kind: Job
metadata:
name: multiline
spec:
template:
spec:
containers:
- command:
- /bin/bash
- -exc
- |
set +x
echo "running below scripts"
if [[ -f "if-condition.sh" ]]; then
echo "Running if success"
else
echo "Running if failed"
fi
name: ubuntu
image: ubuntu
restartPolicy: Never
backoffLimit: 1
这是另一种方法,使用输出日志记录。
apiVersion: v1
kind: Pod
metadata:
labels:
type: test
name: nginx
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: log-vol
mountPath: /var/mylog
command:
- /bin/sh
- -c
- >
i=0;
while [ $i -lt 100 ];
do
echo "hello $i";
echo "$i : $(date)" >> /var/mylog/1.log;
echo "$(date)" >> /var/mylog/2.log;
i=$((i+1));
sleep 1;
done
dnsPolicy: ClusterFirst
restartPolicy: Always
volumes:
- name: log-vol
emptyDir: {}
在这个官方文档中,可以运行在yaml配置文件中命令:
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}\""]
如果我想运行多个命令,怎么办?
command: ["/bin/sh","-c"]
args: ["command one; command two && command three"]
解释: command ["/bin/sh", "-c"]
表示 "run a shell, and execute the following instructions"。然后将 args 作为命令传递给 shell。在 shell 脚本中,分号分隔命令,并且 &&
有条件地 运行 如果第一个命令成功,则执行以下命令。在上面的例子中,它总是 运行s command one
后跟 command two
,只有 运行s command three
如果 command two
成功。
备选方案: 在许多情况下,您想要 运行 的某些命令可能会将最终命令设置为 运行。在这种情况下,特别是构建您自己的 Dockerfile is the way to go. Look at the RUN 指令。
如果您愿意使用 Volume 和 ConfigMap,您可以 mount ConfigMap data 作为脚本,然后 运行 该脚本:
---
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
这稍微清理了您的 pod 规范并允许编写更复杂的脚本。
$ kubectl logs my-pod
Do this
Do that
我更喜欢将参数多行化,这样最简单也最容易阅读。另外,可以在不影响镜像的情况下修改脚本,只需要重启pod即可。例如,对于 mysql 转储,容器规范可能是这样的:
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:
- ...
这样做的原因是 yaml 实际上将“-”之后的所有行连接成一个,而 sh 运行一个长字符串 "echo starting; ls... ; echo done;"。
如果您想避免使用 ;
或 &&
将所有命令连接成一个命令,您还可以使用 heredoc 获得真正的多行脚本:
command:
- sh
- "-c"
- |
/bin/bash <<'EOF'
# Normal script content possible here
echo "Hello world"
ls -l
exit 123
EOF
这对于 运行 现有的 bash 脚本很方便,但缺点是需要内部和外部 shell 实例来设置 heredoc。
只是为了提供另一种可能的选择,可以使用秘密,因为它们以卷的形式呈现给 pod:
秘密示例:
apiVersion: v1
kind: Secret
metadata:
name: secret-script
type: Opaque
data:
script_text: <<your script in b64>>
Yaml 提取:
....
containers:
- name: container-name
image: image-name
command: ["/bin/bash", "/your_script.sh"]
volumeMounts:
- name: vsecret-script
mountPath: /your_script.sh
subPath: script_text
....
volumes:
- name: vsecret-script
secret:
secretName: secret-script
我知道很多人会争辩说这不是必须使用秘密的目的,但它是一种选择。
恕我直言,最好的选择是使用 YAML 的原生 block scalars。具体在这种情况下,folded 样式块。
通过调用 sh -c
,您可以将参数作为命令传递给您的容器,但如果您想用换行符优雅地分隔它们,您需要使用 折叠样式块,这样 YAML 就会知道将换行符转换为空格,从而有效地连接命令。
一个完整的工作示例:
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
app: myapp
spec:
containers:
- name: busy
image: busybox:1.28
command: ["/bin/sh", "-c"]
args:
- >
command_1 &&
command_2 &&
...
command_n
Here is my successful run
apiVersion: v1
kind: Pod
metadata:
labels:
run: busybox
name: busybox
spec:
containers:
- command:
- /bin/sh
- -c
- |
echo "running below scripts"
i=0;
while true;
do
echo "$i: $(date)";
i=$((i+1));
sleep 1;
done
name: busybox
image: busybox
我不确定这个问题是否仍然有效,但由于我没有在上述答案中找到解决方案,我决定把它写下来。
我使用以下方法:
readinessProbe:
exec:
command:
- sh
- -c
- |
command1
command2 && command3
我知道我的示例与 readinessProbe、livenessProbe 等相关,但怀疑容器命令也是如此。这提供了灵活性,因为它反映了在 Bash.
中编写的标准脚本这是运行多行命令的另一种方法。
apiVersion: batch/v1 kind: Job metadata: name: multiline spec: template: spec: containers: - command: - /bin/bash - -exc - | set +x echo "running below scripts" if [[ -f "if-condition.sh" ]]; then echo "Running if success" else echo "Running if failed" fi name: ubuntu image: ubuntu restartPolicy: Never backoffLimit: 1
这是另一种方法,使用输出日志记录。
apiVersion: v1
kind: Pod
metadata:
labels:
type: test
name: nginx
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: log-vol
mountPath: /var/mylog
command:
- /bin/sh
- -c
- >
i=0;
while [ $i -lt 100 ];
do
echo "hello $i";
echo "$i : $(date)" >> /var/mylog/1.log;
echo "$(date)" >> /var/mylog/2.log;
i=$((i+1));
sleep 1;
done
dnsPolicy: ClusterFirst
restartPolicy: Always
volumes:
- name: log-vol
emptyDir: {}