本系列文章探讨了企业客户在使用Kubernetes时遇到的一些常见问题。Container Service客户经常提出的一个问题是,“我如何处理服务之间的依赖关系?”
在应用程序中,组件依赖性指的是中间件服务和业务服务。在传统的软件部署方法中,必须按特定顺序完成应用程序启动和停止任务。
当使用Kubernetes,Docker Swarm和其他容器编排技术在分布式环境中部署应用程序时,不同的组件会同时启动,因此无法确保某个启动顺序。此外,当应用程序运行时,它们所依赖的服务可能会失败或被迁移。因此,解决容器之间的服务依赖性是客户经常提出的问题。
我们可以在应用程序启动逻辑中添加服务依赖性检查逻辑。如果无法访问应用程序所需的服务,则会重试该服务。如果在经过一定次数的重试后服务仍然无法访问,则应用程序会自动放弃。根据容器的重启策略,Kubernetes和Docker等待一段时间后再自动放弃。
在下文中,我们使用一个简单的Golang应用程序作为示例来检查MySQL服务依赖性是否已准备就绪。
...
// Connect to database.
hostPort := net.JoinHostPort(config.Host, config.Port)
log.Println("Connecting to database at", hostPort)
dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?timeout=30s",
config.Username, config.Password, hostPort, config.Database)
db, err = sql.Open("mysql", dsn)
if err != nil {
log.Println(err)
}
var dbError error
maxAttempts := 20
for attempts := 1; attempts <= maxAttempts; attempts++ {
dbError = db.Ping()
if dbError == nil {
break
}
log.Println(dbError)
time.Sleep(time.Duration(attempts) * time.Second)
}
if dbError != nil {
log.Fatal(dbError)
}
log.Println("Application started successfully.")
...
“快速失败”是契约式设计的一项重要原则,有助于确保系统的稳健性和可预测性。在上面的代码中,如果重试机制失败,则报告log.Fatal(dbError)
并且该过程结束。此外,K8S和Docker容器重新启动回滚功能可确保系统资源不会因重复尝试访问应用程序依赖项而失败。
在现实世界中,一些遗留应用程序和框架无法调整。因此,我们希望将其检查策略和应用程序逻辑分离。
一种常见方法是在容器的Dockerfile启动脚本中添加相关的服务依赖性检查逻辑。有关此方法的详细信息,请参阅此Docker文档。另一种方法是使用Kubernetes pod机制本身来添加依赖性检查逻辑。
在开始之前,我们必须了解pod生命周期。
首先,pod包含三种类型的容器:
Kubernetes的最佳实践通常依赖于初始化容器来检查服务依赖性。我们使用以下WordPress示例来说明如何完成此操作。
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
clusterIP: None
ports:
- name: mysql
port: 3306
selector:
app: mysql
---
apiVersion: v1
kind: Service
metadata:
name: wordpress
spec:
ports:
- name: wordpress
port: 80
targetPort: 80
selector:
app: wordpress
type: NodePort
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
serviceName: mysql
replicas: 1
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ALLOW_EMPTY_PASSWORD
value: "true"
livenessProbe:
exec:
command: ["mysqladmin", "ping"]
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
exec:
# Check we can execute queries over TCP (skip-networking is off).
command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
initialDelaySeconds: 5
periodSeconds: 2
timeoutSeconds: 1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
spec:
replicas: 1
selector:
matchLabels:
app: wordpress
template:
metadata:
labels:
app: wordpress
spec:
containers:
- name: wordpress
image: wordpress:4
ports:
- containerPort: 80
env:
- name: WORDPRESS_DB_HOST
value: mysql
- name: WORDPRESS_DB_PASSWORD
value: ""
initContainers:
- name: init-mysql
image: busybox
command: ['sh', '-c', 'until nslookup mysql; do echo waiting for mysql; sleep 2; done;']
在WordPress部署窗格定义中,我们添加了initContainers
。这将检查是否可以解析MySQL
域名以确定MySQL服务依赖性是否已准备就绪。
与此同时,我们在MySQL StatefulSet中引入了一个readinessProbe
和livenessProbe
以确定MySQL进程是否已为业务做好准备。在K8S中,只要pod是健康的,它就可以执行ClusterIP访问或DNS解析。
$ kubectl create -f wordpress.yaml
service "mysql" created
service "wordpress" created
statefulset "mysql" created
deployment "wordpress" created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-0 0/1 Running 0 5s
wordpress-797655cf44-w4p87 0/1 Init:0/1 0 5s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-0 1/1 Running 0 11s
wordpress-797655cf44-w4p87 0/1 Init:0/1 0 11s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-0 1/1 Running 0 14s
wordpress-797655cf44-w4p87 0/1 PodInitializing 0 14s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-0 1/1 Running 0 17s
wordpress-797655cf44-w4p87 1/1 Running 0 17s
$ kubectl describe pods wordpress-797655cf44-w4p87
...
注意:
本文讨论了用于检查服务依赖性的常用解决方案,并提供了一个示例来演示如何使用init容器,活动性和就绪性探针以及其他服务运行状况检查和依赖性检查功能。
Kubernetes提供灵活的pod生命周期管理功能。由于篇幅限制,我们没有讨论postStart,preStop和其他生命周期曲线。
原文标题《Kubernetes Demystified: Solving Service Dependencies》
作者:Leona Zhang
译者:February
不代表云加社区观点,更多详情请查看原文链接
本文系外文翻译,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系外文翻译,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。