简介
本教程将指导你在 Kubernetes 容器(pod)启动时运行命令。你将学习如何使用 command 和 args 字段配置启动命令、执行多个命令以及处理启动失败的情况。我们将使用 Minikube 创建一个本地 Kubernetes 环境,让你可以亲身体验这些概念。
在本实验结束时,你将了解如何在启动期间自定义 Kubernetes 容器的行为,并确保应用程序顺利部署。这些知识对于在 Kubernetes 环境中正确配置容器化应用程序至关重要。
设置你的 Kubernetes 环境
在这一步,我们将通过启动 Minikube 并了解本实验所需的基本 Kubernetes 概念来准备我们的环境。
什么是 Kubernetes?
Kubernetes 是一个开源平台,旨在自动化应用程序容器的部署、扩展和运维。Kubernetes 的核心是容器(Pod)——可以创建和管理的最小可部署单元。
启动 Minikube
Minikube 是一个让你在本地运行 Kubernetes 的工具。让我们启动它:
minikube start --driver=docker
此命令将使用 Docker 作为驱动程序创建一个本地 Kubernetes 集群。启动过程可能需要几分钟,因为它会下载必要的组件。
你应该会看到类似以下的输出:
😄 minikube v1.30.1 on Ubuntu 22.04
✨ Using the docker driver based on user configuration
📌 Using Docker driver with root privileges
👍 Starting control plane node minikube in cluster minikube
🚜 Pulling base image ...
🔥 Creating docker container (CPUs=2, Memory=2200MB) ...
🐳 Preparing Kubernetes v1.27.4 on Docker 24.0.4 ...
▪ Generating certificates and keys ...
▪ Booting up control plane ...
▪ Configuring RBAC rules ...
🔎 Verifying Kubernetes components...
🌟 Enabled addons: default-storageclass, storage-provisioner
💡 kubectl not found. If you need it, try: 'minikube kubectl -- get pods -A'
🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
验证安装
让我们通过检查 Minikube 集群的状态来验证一切是否正常工作:
minikube status
你应该会看到表明 Minikube 正在运行的输出:
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
现在让我们检查 kubectl(Kubernetes 命令行工具)是否已正确配置:
kubectl get nodes
你应该会看到一个节点(你的 Minikube 实例)显示为 Ready:
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane 2m15s v1.27.4
理解 Kubernetes 容器(Pod)
容器(Pod)是 Kubernetes 应用程序的基本执行单元。每个容器代表集群上的一个运行进程,并封装了一个或多个容器。容器在设计上是短暂的,这意味着它们可以根据需要创建、销毁和重新创建。
在 Kubernetes 中,你通常在 YAML 文件中定义容器。对于本实验,我们已经在你的环境中准备了一些示例 YAML 文件。
让我们进入这些文件所在的目录:
cd ~/project/kubernetes-examples
ls -la
你应该会看到以下文件:
total 20
drwxr-xr-x 2 labex labex 4096 Sep 21 10:00 .
drwxr-xr-x 3 labex labex 4096 Sep 21 10:00 ..
-rw-r--r-- 1 labex labex 193 Sep 21 10:00 basic-pod.yaml
-rw-r--r-- 1 labex labex 254 Sep 21 10:00 liveness-probe-pod.yaml
-rw-r--r-- 1 labex labex 312 Sep 21 10:00 multi-command-pod.yaml
-rw-r--r-- 1 labex labex 263 Sep 21 10:00 startup-command-pod.yaml
现在我们的环境已经设置好,我们准备好开始使用 Kubernetes 容器并学习如何在启动时运行命令了。
使用基本启动命令创建你的第一个容器(Pod)
在这一步,我们将创建第一个 Kubernetes 容器(Pod),并了解它如何在启动时执行命令。我们将研究 command 字段,该字段定义了容器启动时应运行的内容。
探索基本容器配置
首先,让我们查看一个基本容器的配置。使用 nano 编辑器打开 basic-pod.yaml 文件:
nano basic-pod.yaml
你会看到一个包含以下内容的 YAML 文件:
apiVersion: v1
kind: Pod
metadata:
name: basic-pod
spec:
containers:
- name: ubuntu
image: ubuntu:20.04
command: ["/bin/bash", "-c", "echo 'Pod is running' && sleep 3600"]
此配置定义了:
- 一个名为
basic-pod的容器(Pod) - 一个名为
ubuntu的单个容器,使用ubuntu:20.04镜像 - 一个启动命令,该命令会输出一条消息,然后休眠 3600 秒(1 小时)
command 字段指定了容器启动时将运行的可执行文件。在这种情况下,我们使用 -c 标志运行 /bin/bash shell,这允许我们传递一串要执行的命令。
按 Ctrl+X 退出 nano 编辑器。
创建容器
让我们在 Kubernetes 集群中创建这个容器:
kubectl apply -f basic-pod.yaml
你应该会看到类似以下的输出:
pod/basic-pod created
检查容器状态
现在,让我们检查容器是否正常运行:
kubectl get pods
你应该会看到类似以下的输出:
NAME READY STATUS RESTARTS AGE
basic-pod 1/1 Running 0 30s
这表明我们的容器正在成功运行。READY 列下的 1/1 表示在预期的一个容器中,有一个容器正在运行。
查看容器日志
要查看启动命令的输出,我们可以检查容器的日志:
kubectl logs basic-pod
你应该会看到:
Pod is running
这证实了我们的启动命令已成功执行。
探索正在运行的容器
让我们通过执行交互式 shell 来查看容器内部正在发生的事情:
kubectl exec -it basic-pod -- /bin/bash
现在你已进入容器的 shell。让我们验证我们的进程是否正在运行:
ps aux
你应该会看到休眠命令正在运行,它是我们启动命令的一部分:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 4112 3372 ? Ss 10:05 0:00 /bin/bash -c echo 'Pod is running' && sleep 3600
root 7 0.0 0.0 4112 3536 pts/0 Ss 10:06 0:00 /bin/bash
root 14 0.0 0.0 5900 2928 pts/0 R+ 10:06 0:00 ps aux
root 15 0.0 0.0 2512 580 ? S 10:06 0:00 sleep 3600
PID 为 1 的进程是我们的启动命令,sleep 3600 命令作为一个单独的进程正在运行。
通过输入以下命令退出容器 shell:
exit
理解容器生命周期
当你创建一个容器时,它会经历几个阶段:
- Pending(挂起):容器已被 Kubernetes 接受,但尚未运行
- Running(运行中):容器正在运行,所有容器都已启动
- Succeeded(成功):容器中的所有容器都已成功终止
- Failed(失败):所有容器都已终止,并且至少有一个容器失败
- Unknown(未知):无法确定容器的状态
我们的容器处于 Running 状态,因为我们的主进程(sleep 3600)仍在运行。当此进程完成或被终止时,容器将根据退出代码转换为 Succeeded 或 Failed 状态。
现在你已经了解了如何创建一个基本容器,并使用 command 字段在启动时运行命令。
使用 command 和 args 进行启动配置
在这一步,你将学习如何结合使用 command 和 args 字段,在 Kubernetes 容器(Pod)中配置更复杂的启动行为。
理解 command 和 args
Kubernetes 提供了两种主要方式来指定容器启动时运行的命令:
- **
command**:指定要运行的可执行文件(类似于 Docker 的ENTRYPOINT) - **
args**:指定传递给命令的参数(类似于 Docker 的CMD)
单独或结合使用这些字段,能让你灵活地控制容器的启动方式。
探索启动命令容器
让我们查看 startup-command-pod.yaml 文件:
nano startup-command-pod.yaml
你会看到以下配置:
apiVersion: v1
kind: Pod
metadata:
name: startup-command-pod
spec:
containers:
- name: ubuntu
image: ubuntu:20.04
command: ["/bin/bash"]
args:
["-c", "echo 'Custom startup message' > /tmp/startup.txt && sleep 3600"]
在此配置中:
command字段指定要运行/bin/bashargs字段提供传递给bash的参数:-c和包含命令的字符串
这种将命令和参数分开的方式,使配置更易读、更易维护。
按 Ctrl+X 退出 nano 编辑器。
创建启动命令容器
让我们创建这个容器:
kubectl apply -f startup-command-pod.yaml
你应该会看到:
pod/startup-command-pod created
检查容器状态
验证容器是否正在运行:
kubectl get pods startup-command-pod
你应该会看到:
NAME READY STATUS RESTARTS AGE
startup-command-pod 1/1 Running 0 30s
验证启动命令
现在,让我们通过检查是否创建了预期的文件,来验证启动命令是否正确执行:
kubectl exec startup-command-pod -- cat /tmp/startup.txt
你应该会看到:
Custom startup message
这证实了命令已成功执行,并创建了包含指定内容的文件。
理解 command 和 args 的区别
理解 command 和 args 之间的关系很重要:
- 如果你只指定
command,它将覆盖容器镜像的默认ENTRYPOINT - 如果你只指定
args,它将覆盖容器镜像的默认CMD - 如果你同时指定
command和args,它们将同时覆盖ENTRYPOINT和CMD
让我们看看修改容器配置会发生什么。创建一个名为 modified-command-pod.yaml 的新文件:
nano modified-command-pod.yaml
添加以下内容:
apiVersion: v1
kind: Pod
metadata:
name: modified-command-pod
spec:
containers:
- name: ubuntu
image: ubuntu:20.04
command: ["/bin/echo"]
args: ["This message is printed by echo"]
按 Ctrl+X,然后按 Y 和 Enter 保存并退出。
现在,让我们创建这个容器:
kubectl apply -f modified-command-pod.yaml
你应该会看到:
pod/modified-command-pod created
查看日志,了解发生了什么:
kubectl logs modified-command-pod
你应该会看到:
This message is printed by echo
注意容器如何使用指定的参数执行 /bin/echo 命令,然后在 echo 打印输出后终止。
检查容器状态:
kubectl get pods modified-command-pod
你应该会看到类似以下内容:
NAME READY STATUS RESTARTS AGE
modified-command-pod 0/1 Completed 0 45s
Completed 状态表示容器已运行完成并成功退出。
现在你已经了解了如何结合使用 command 和 args 来配置 Kubernetes 容器的启动行为。
执行多条命令并处理启动失败情况
在这一步,你将学习如何在容器(Pod)启动时执行多条命令,以及如何使用健康检查来处理启动失败的情况。
执行多条命令
通常,你需要在容器启动时运行多条命令。在 Kubernetes 中有几种方法可以实现这一点:
- 使用 shell 操作符(
&&、;等)来链接命令 - 使用脚本文件
- 使用初始化容器(init container)
让我们通过查看 multi-command-pod.yaml 文件来探索第一种方法:
nano multi-command-pod.yaml
你会看到:
apiVersion: v1
kind: Pod
metadata:
name: multi-command-pod
spec:
containers:
- name: ubuntu
image: ubuntu:20.04
command: ["/bin/bash", "-c"]
args:
[
"echo 'First command' > /tmp/first.txt && echo 'Second command' > /tmp/second.txt && sleep 3600"
]
此配置使用 && 操作符将多条命令链接在一起,只有当前一条命令成功执行时,才会执行下一条命令。
按 Ctrl+X 退出编辑器。
让我们创建这个容器:
kubectl apply -f multi-command-pod.yaml
你应该会看到:
pod/multi-command-pod created
现在,让我们通过检查创建的文件来验证两条命令是否都已执行:
kubectl exec multi-command-pod -- cat /tmp/first.txt
你应该会看到:
First command
对于第二个文件:
kubectl exec multi-command-pod -- cat /tmp/second.txt
你应该会看到:
Second command
使用健康检查处理启动失败情况
Kubernetes 提供了检测和处理启动失败的机制:
- 存活探针(Liveness Probes):检查容器是否正在运行;如果没有运行,Kubernetes 将重启它
- 就绪探针(Readiness Probes):检查容器是否准备好接收流量
- 启动探针(Startup Probes):检查应用程序是否已启动;对于启动缓慢的容器很有用
让我们查看一个使用存活探针的容器:
nano liveness-probe-pod.yaml
你会看到:
apiVersion: v1
kind: Pod
metadata:
name: liveness-probe-pod
spec:
containers:
- name: ubuntu
image: ubuntu:20.04
command: ["/bin/bash", "-c"]
args: ["touch /tmp/healthy && sleep 3600"]
livenessProbe:
exec:
command: ["cat", "/tmp/healthy"]
initialDelaySeconds: 5
periodSeconds: 5
在此配置中:
- 容器在启动时创建一个文件
/tmp/healthy - 存活探针每 5 秒检查一次该文件
- 如果文件缺失,Kubernetes 会认为容器不健康并重启它
按 Ctrl+X 退出编辑器。
让我们创建这个容器:
kubectl apply -f liveness-probe-pod.yaml
你应该会看到:
pod/liveness-probe-pod created
检查容器是否正在运行:
kubectl get pods liveness-probe-pod
你应该会看到:
NAME READY STATUS RESTARTS AGE
liveness-probe-pod 1/1 Running 0 30s
现在,让我们看看如果删除健康检查文件会发生什么:
kubectl exec liveness-probe-pod -- rm /tmp/healthy
等待约 10 秒,然后再次检查容器状态:
kubectl get pods liveness-probe-pod
你应该会看到容器已被重启:
NAME READY STATUS RESTARTS AGE
liveness-probe-pod 1/1 Running 1 60s
RESTARTS 计数已增加到 1,表明 Kubernetes 检测到容器处于不健康状态并重启了它。
让我们验证健康检查文件是否再次存在(容器重启时,启动命令应该会重新创建该文件):
kubectl exec liveness-probe-pod -- ls -la /tmp/healthy
你应该会看到文件再次存在:
-rw-r--r-- 1 root root 0 Sep 21 10:30 /tmp/healthy
这展示了 Kubernetes 如何自动从启动失败中恢复,并维持应用程序的期望状态。
创建自定义启动脚本
对于更复杂的初始化操作,你可能需要使用自定义启动脚本。让我们创建一个使用 shell 脚本进行启动的容器:
nano script-pod.yaml
添加以下内容:
apiVersion: v1
kind: Pod
metadata:
name: script-pod
spec:
containers:
- name: ubuntu
image: ubuntu:20.04
command: ["/bin/bash", "-c"]
args:
- |
cat > /tmp/startup.sh << 'EOF'
#!/bin/bash
echo "Script started at $(date)" > /tmp/script-log.txt
echo "Creating configuration files..." >> /tmp/script-log.txt
mkdir -p /tmp/config
echo "app_name=MyApp" > /tmp/config/app.conf
echo "version=1.0" >> /tmp/config/app.conf
echo "Script completed successfully" >> /tmp/script-log.txt
EOF
chmod +x /tmp/startup.sh
/tmp/startup.sh
sleep 3600
此配置:
- 在容器中创建一个启动脚本
- 使其可执行
- 运行该脚本
- 通过
sleep命令保持容器运行
按 Ctrl+X,然后按 Y 和 Enter 保存并退出。
让我们创建这个容器:
kubectl apply -f script-pod.yaml
你应该会看到:
pod/script-pod created
等待片刻,让容器启动,然后检查其状态:
kubectl get pods script-pod
你应该会看到:
NAME READY STATUS RESTARTS AGE
script-pod 1/1 Running 0 30s
现在,让我们检查脚本的输出:
kubectl exec script-pod -- cat /tmp/script-log.txt
你应该会看到类似以下内容:
Script started at Tue Sep 21 10:35:42 UTC 2023
Creating configuration files...
Script completed successfully
让我们验证配置文件是否已创建:
kubectl exec script-pod -- cat /tmp/config/app.conf
你应该会看到:
app_name=MyApp
version=1.0
这展示了如何使用复杂的启动脚本来初始化 Kubernetes 中的容器。
最佳实践与实际应用
在最后这一步,你将探索在 Kubernetes 容器(Pod)启动时运行命令的最佳实践,并创建一个实现这些实践的实际应用示例。
启动命令的最佳实践
在为 Kubernetes 容器配置启动命令时,请考虑以下最佳实践:
- 保持启动命令幂等:命令应能安全地多次运行而不引发问题。
- 使用健康检查:实现存活探针(liveness)和就绪探针(readiness)以验证启动是否成功。
- 优雅地处理失败:在启动脚本中包含错误处理机制。
- 分离关注点:使用初始化容器(init container)来执行与主应用程序分离的初始化任务。
- 限制启动时间:保持初始化过程快速,以减少部署时间。
- 使用环境变量:通过环境变量使启动命令可配置。
- 记录启动进度:输出清晰的日志以便进行故障排除。
使用初始化容器执行启动任务
初始化容器在应用容器启动之前运行,非常适合用于设置任务。让我们创建一个带有初始化容器的容器:
nano init-container-pod.yaml
添加以下内容:
apiVersion: v1
kind: Pod
metadata:
name: init-container-pod
spec:
initContainers:
- name: init-config
image: ubuntu:20.04
command: ["/bin/bash", "-c"]
args:
- |
echo "Initializing configuration..."
mkdir -p /work-dir/config
echo "database_url=mysql://user:password@db:3306/mydb" > /work-dir/config/db.conf
echo "api_key=1234567890" > /work-dir/config/api.conf
echo "Initialization complete"
volumeMounts:
- name: shared-volume
mountPath: /work-dir
containers:
- name: app
image: ubuntu:20.04
command: ["/bin/bash", "-c"]
args:
- |
echo "Application starting..."
echo "Reading configuration:"
cat /work-dir/config/db.conf
cat /work-dir/config/api.conf
echo "Application running..."
sleep 3600
volumeMounts:
- name: shared-volume
mountPath: /work-dir
volumes:
- name: shared-volume
emptyDir: {}
在此配置中:
- 初始化容器
init-config首先运行并创建配置文件。 - 两个容器共享一个名为
shared-volume的卷。 - 主应用容器读取由初始化容器创建的配置。
按 Ctrl+X,然后按 Y 和 Enter 保存并退出。
让我们创建这个容器:
kubectl apply -f init-container-pod.yaml
你应该会看到:
pod/init-container-pod created
检查容器状态:
kubectl get pods init-container-pod
你应该会看到容器正在运行:
NAME READY STATUS RESTARTS AGE
init-container-pod 1/1 Running 0 30s
现在,让我们检查主容器的日志:
kubectl logs init-container-pod -c app
你应该会看到类似以下内容:
Application starting...
Reading configuration:
database_url=mysql://user:password@db:3306/mydb
api_key=1234567890
Application running...
这证实了初始化容器成功创建了配置文件,并且主容器能够读取这些文件。
实际示例:带有数据库检查的 Web 应用程序
让我们创建一个更实际的示例——一个在启动前检查数据库可用性的 Web 应用程序:
nano webapp-pod.yaml
添加以下内容:
apiVersion: v1
kind: Pod
metadata:
name: webapp-pod
spec:
initContainers:
- name: wait-for-db
image: busybox:1.28
command: ["/bin/sh", "-c"]
args:
- |
echo "Checking for database availability..."
## In a real scenario, this would check an actual database
## For this example, we'll simulate success after a short delay
sleep 5
echo "Database is available"
touch /tmp/db-ready
volumeMounts:
- name: shared-volume
mountPath: /tmp
containers:
- name: webapp
image: nginx:1.19
ports:
- containerPort: 80
command: ["/bin/sh", "-c"]
args:
- |
if [ -f /tmp/db-ready ]; then
echo "Database connection verified, starting web application..."
## Customize nginx configuration
echo "<h1>Web Application Started Successfully</h1>" > /usr/share/nginx/html/index.html
echo "<p>Connected to database</p>" >> /usr/share/nginx/html/index.html
## Start nginx
nginx -g 'daemon off;'
else
echo "Error: Database not available"
exit 1
fi
volumeMounts:
- name: shared-volume
mountPath: /tmp
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: shared-volume
emptyDir: {}
此配置:
- 使用初始化容器检查数据库可用性(模拟)。
- 主容器在启动前检查初始化容器创建的文件。
- 包含一个就绪探针以验证应用程序是否正在提供流量。
- 使用共享卷在容器之间进行通信。
按 Ctrl+X,然后按 Y 和 Enter 保存并退出。
让我们创建这个容器:
kubectl apply -f webapp-pod.yaml
你应该会看到:
pod/webapp-pod created
等待片刻,让容器完全启动,然后检查其状态:
kubectl get pods webapp-pod
你应该会看到:
NAME READY STATUS RESTARTS AGE
webapp-pod 1/1 Running 0 30s
READY 列中的 1/1 表示就绪探针已成功。
让我们进行端口转发以访问 Web 应用程序:
kubectl port-forward webapp-pod 8080:80 &
此命令在后台运行(由于 &)。现在,你可以使用 curl 访问 Web 应用程序:
curl http://localhost:8080
你应该会看到:
<h1>Web Application Started Successfully</h1>
<p>Connected to database</p>
这证实了应用程序已正确初始化,验证了数据库的可用性,并且现在正在提供流量。
停止端口转发进程:
pkill -f "kubectl port-forward"
清理
在结束实验之前,让我们清理创建的资源:
kubectl delete pod basic-pod startup-command-pod modified-command-pod multi-command-pod liveness-probe-pod script-pod init-container-pod webapp-pod
你应该会看到:
pod "basic-pod" deleted
pod "startup-command-pod" deleted
pod "modified-command-pod" deleted
pod "multi-command-pod" deleted
pod "liveness-probe-pod" deleted
pod "script-pod" deleted
pod "init-container-pod" deleted
pod "webapp-pod" deleted
现在你已经学会了如何在 Kubernetes 容器启动时运行命令、执行多条命令、处理失败情况,以及在实际场景中应用最佳实践。
总结
在本次实验中,你学习了如何在 Kubernetes 容器(Pod)启动时运行命令。你获得了以下方面的实践经验:
- 使用 Minikube 设置 Kubernetes 环境
- 创建并管理带有特定启动命令的容器
- 使用
command和args字段配置容器行为 - 使用 shell 操作符和脚本执行多条命令
- 使用健康检查处理启动失败情况
- 使用初始化容器(init container)实施最佳实践
- 构建需要正确初始化的实际应用程序
这些技能对于在 Kubernetes 环境中部署容器化应用程序至关重要。通过正确配置启动命令,你可以确保应用程序正确初始化、验证依赖项并优雅地处理错误。
在你继续 Kubernetes 学习之旅时,请记住,正确的应用程序初始化是构建可靠、可扩展和可维护系统的关键部分。


