生产环境中使用Kubernetes - Working with Containers in Production
持久存储
容器文件系统的生命周期与容器的生命周期一样长,因此当容器崩溃并重新启动时,对文件系统的更改将丢失,容器将从全新的状态重新启动。要在容器文件系统之外访问更持久的存储,您需要一个 卷。这对于有状态的应用程序尤其重要,例如键值存储和数据库。
例如, Redis是一个键值缓存和存储,我们在 留言簿和其他示例中使用它。我们可以向它添加一个卷来存储持久数据,如下所示:
redis-deployment.yaml |
---|
|
emptyDir
卷的生命周期是 pod的生命周期,它比任何一个容器的生命周期都要长,所以如果容器失败并重新启动,我们的存储将继续存在。
除了 Kubernetes 提供的本地磁盘存储外 emptyDir
,Kubernetes 还支持许多不同的网络附加存储解决方案,包括 GCE 上的 PD 和 EC2 上的 EBS,它们是关键数据的首选,并会处理节点上的设备挂载和卸载等细节. 有关更多详细信息,请参 阅卷文档。
分发凭据
许多应用程序需要凭据(例如密码、OAuth 令牌和 TLS 密钥)来与其他应用程序、数据库和服务进行身份验证。将这些凭据存储在容器映像或环境变量中并不理想,因为任何有权访问映像、pod/容器规范、主机文件系统或主机 Docker 守护程序的人都可以复制这些凭据。
Kubernetes 提供了一种称为 secrets的机制,可以方便地将敏感凭证传递给应用程序。A Secret
是一个包含数据映射的简单资源。例如,您可以使用用户名和密码创建一个简单的秘密,如下所示:
$ kubectl create secret generic mysecret --from-literal=username="admin",password="1234"
secret "mysecret" created
这相当于 kubectl create -f
:
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MTIzNA==
与其他资源一样,创建的秘密可以通过以下方式查看 get
:
$ kubectl get secrets
NAME TYPE DATA AGE
default-token-zirbw kubernetes.io/service-account-token 3 3h
mysecret Opaque 2 2m
要使用密钥,您需要在 pod 或 pod 模板中引用它。卷 secret
源使您可以将其作为内存目录挂载到容器中。
redis-secret-deployment.yaml |
---|
|
使用私有镜像注册中心进行身份验证
秘密也可用于传递 图像注册表凭据。
为 Docker 注册表创建 secret 的最简单方法是:
$ kubectl create secret docker-registry myregistrykey --docker-username=janedoe --docker-password=●●●●●●●●●●● [email protected]
secret "myregistrykey" created
或者,您可以通过以下步骤执行等效操作。首先,创建一个 .docker/config.json
,例如通过运行 docker login <registry.domain>
。然后将生成的 .docker/config.json
文件放入一个 秘密资源中。例如:
$ docker login
Username: janedoe
Password: ●●●●●●●●●●●
Email: [email protected]
WARNING: login credentials saved in /Users/jdoe/.docker/config.json.
Login Succeeded
$ echo $(cat ~/.docker/config.json)
{ "https://index.docker.io/v1/": { "auth": "ZmFrZXBhc3N3b3JkMTIK", "email": "[email protected]" } }
$ cat ~/.docker/config.json | base64
eyAiaHR0cHM6Ly9pbmRleC5kb2NrZXIuaW8vdjEvIjogeyAiYXV0aCI6ICJabUZyWlhCaGMzTjNiM0prTVRJSyIsICJlbWFpbCI6ICJqZG9lQGV4YW1wbGUuY29tIiB9IH0K
$ cat > /tmp/image-pull-secret.yaml <<EOF
apiVersion: v1
kind: Secret
metadata:
name: myregistrykey
data:
.dockerconfigjson: eyAiaHR0cHM6Ly9pbmRleC5kb2NrZXIuaW8vdjEvIjogeyAiYXV0aCI6ICJabUZyWlhCaGMzTjNiM0prTVRJSyIsICJlbWFpbCI6ICJqZG9lQGV4YW1wbGUuY29tIiB9IH0K
type: kubernetes.io/dockerconfigjson
EOF
$ kubectl create -f /tmp/image-pull-secret.yaml
secret "myregistrykey" created
imagePullSecrets
现在,您可以通过在 pod 定义中添加一个部分来创建引用该机密的 pod。
apiVersion: v1
kind: Pod
metadata:
name: foo
spec:
containers:
- name: foo
image: janedoe/awesomeapp:v1
imagePullSecrets:
- name: myregistrykey
辅助容器
Pod支持同时运行多个容器。它们可用于托管垂直集成的应用程序堆栈,但它们的主要动机是支持辅助主要应用程序的辅助帮助程序。典型的例子是数据拉取器、数据推送器和代理。
这样的容器通常需要相互通信,通常是通过文件系统。这可以通过将相同的卷安装到两个容器中来实现。这种模式的一个例子是一个 Web 服务器,它带有一个 轮询 git 存储库以获取新更新的程序:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: my-nginx
spec:
template:
metadata:
labels:
app: nginx
spec:
volumes:
- name: www-data
emptyDir: {}
containers:
- name: nginx
image: nginx
# This container reads from the www-data volume
volumeMounts:
- mountPath: /srv/www
name: www-data
readOnly: true
- name: git-monitor
image: myrepo/git-monitor
env:
- name: GIT_REPO
value: http://github.com/some/repo.git
# This container writes to the www-data volume
volumeMounts:
- mountPath: /data
name: www-data
资源管理
Kubernetes 的调度器只会将应用程序放置在它们有足够 CPU 和内存的地方,但它只有在知道它们需要多少 资源的情况下才能这样做。指定太少 CPU 的后果是,如果太多其他容器被调度到同一节点上,容器可能会缺乏 CPU。同样,如果没有请求内存,容器可能会因内存不足而意外死亡,这对于大内存应用程序尤其可能。
如果没有指定资源需求,则假定资源的名义数量。(此默认值通过默认 Namespace的 LimitRange应用。可以使用 查看。)您可以明确指定所需的资源量,如下所示: kubectl describe limitrange limits
redis-resource-deployment.yaml |
---|
|
如果容器超过其指定的限制,容器将因 OOM(内存不足)而死亡,因此指定一个略高于预期的值通常会提高可靠性。通过指定请求,可以保证 pod 在需要时能够使用那么多资源。有关资源限制和请求之间的区别,请参阅 资源 QoS 。
如果您不确定要请求多少资源,您可以先启动应用程序而不指定资源,然后使用 资源使用监控来确定适当的值。
Liveness 和 Readiness 探测(又名健康检查)
许多长时间运行的应用程序最终会转变为损坏状态,除非重新启动它们,否则无法恢复。Kubernetes 提供了 活性探针来检测和补救这种情况。
探测应用程序的常用方法是使用 HTTP,可以按如下方式指定:
nginx-probe-deployment.yaml |
---|
|
其他时候,应用只是暂时无法服务,会自行恢复。通常在这种情况下,您不希望终止应用程序,但也不想向其发送请求,因为应用程序将无法正确响应或根本无法响应。常见的此类场景是在应用程序启动期间加载大型数据或配置文件。Kubernetes 提供 就绪探针来检测和缓解此类情况。Readiness 探针的配置与 liveness 探针类似,只是使用该 readinessProbe
字段。带有容器报告它们尚未准备好的 pod 将不会通过 Kubernetes 服务接收流量。
有关更多详细信息(例如,如何指定基于命令的探测),请参阅 演练中的示例、 独立示例和 文档。
生命周期钩子和终止通知
当然,节点和应用程序可能随时发生故障,但许多应用程序受益于干净关闭,例如在有意终止应用程序时完成正在进行的请求。为了支持这种情况,Kubernetes 支持两种通知:
- Kubernetes 将向应用程序发送 SIGTERM,可以对其进行处理以实现优雅终止。如果应用程序没有提前终止(默认为 30 秒,由 控制
spec.terminationGracePeriodSeconds
),则会在可配置的秒数后发送 SIGKILL。 - Kubernetes 支持 pre-stop 生命周期钩子的(可选)规范,它将在发送 SIGTERM 之前执行。
pre-stop hook 的规格与probe 的规格类似,但没有与时间相关的参数。例如:
nginx-lifecycle-deployment.yaml |
---|
|
终止消息
为了达到相当高的可用性水平,特别是对于积极开发的应用程序,快速调试故障非常重要。除了常规 日志收集之外, Kubernetes 还可以通过使用UI kubectl
或 UI显示致命错误的原因来加速调试。可以指定容器将在何处写入其“死亡声音”,例如断言失败消息、堆栈跟踪、异常等。默认路径是. terminationMessagePath
/dev/termination-log
这是一个玩具示例:
pod-w-message.yaml |
---|
|
该消息与最后(即最近)终止的其他状态一起记录:
$ kubectl create -f ./pod-w-message.yaml
pod "pod-w-message" created
$ sleep 70
$ kubectl get pods/pod-w-message -o go-template="{{range .status.containerStatuses}}{{.lastState.terminated.message}}{{end}}"
Sleep expired
$ kubectl get pods/pod-w-message -o go-template="{{range .status.containerStatuses}}{{.lastState.terminated.exitCode}}{{end}}"
0