程序部署环境的容器化已经是大势所趋,微服务为容器化提供了广阔的应用舞台,k8s已经把Docker纳入为它的底层支撑容器引擎,一统江湖,成为了容器技术事实上的标准。一般的应用程序是不能直接拿来部署到容器上的,需要经过一些修改才能移植到k8s上。那么这些改动包括哪些内容呢?
它主要有两个部分:
第一部分是服务调用。不论是微服务之间的调用,还是微服务调用数据库或前端调用后端,调用的方式都是一样的。都需要知道IP地址,端口和协议,例如“http://127.0.0.1:80”, 其中“http”是协议,“127.0.0.1”是IP地址,“80”是端口。它的关键是让k8s的配置文件和应用程序都共享相同的调用地址。
第二部分是数据的持久存储。在程序运行时,经常要访问持久存储(硬盘)上的数据,例如日志,配置文件或临时共享数据。程序在容器中运行,一旦出现问题,容器会被摧毁,k8s会自动重新生成一个与原来一模一样的容器,并在上面重新部署应用程序。在集群环境下,用户感觉不到容器故障,因为系统已经自动修复了。但当容器被摧毁时,容器上的数据也一起被摧毁了,因此要保证程序运行的连续性,就要让持久存储不受容器故障的影响。
程序实例:
我们通过一个Go(别的语言也大同小异)微服务程序做例子来展示要做的修改。它本身的功能非常简单,只是用SQL语句访问数据库中的数据,并写入日志。你可以简单地把它分成两层,后端数据访问层和数据库层。在k8s中它被分成两个服务。一个是后端服务程序,另一个是数据库(用MySQL)服务。后端程序要调用数据库服务,然后会把一些数据写入日志,而且这个日志不能因为容器故障而丢失。数据库对数据的保存要求更高,即使k8s集群或虚拟机出了问题或断电也要保证数据的存在。
上面是程序的目录结构。我们重点讲一下与k8s相关的。“config”目录包含与程序配置有关的代码,“logs”目录是用来存储日志文件的,没有代码。“script”目录是重点,里面包含了所有与部署程序相关的文件。其中“database”子目录里面是数据库脚本,“kubernetes”子目录存有k8s的所有配置文件,一回儿还会详细讲解。
服务调用:
服务调用涉及到两个不同的部分。一部分是k8s的配置文件,它负责服务的注册和发现。所有部署在k8s上的应用都通过k8s的服务来进行互相调用。另一部分是应用程序,它需要通过k8s的服务来访问其他程序。在没有k8s时,后端要想访问数据库,代码是这样的:
db, err := sql.Open("mysql", "dbuser:dbuser@tcp(localhost:3306)/service_config?charset=utf8")
其中,“dbuser:dbuser”是数据库用户名和口令,“localhost:3306”是数据库主机名和端口地址,“service-config”是数据库名,共有五个数据需要读取。迁移到k8s之后,我们要把这些参数从程序中提取出来,转化成从k8s中读取相关数据。
k8s配置:
先来看一下k8s的配置文件。
上面就是k8s的配置文件目录结构,最外层(kubernetes目录下)有两个“yaml”文件“k8sdemo-config.yaml”和"k8sdemo-secret.yaml",它们是被不同服务共享的,因此放在最外层。另外还有一个"k8sdemo.sh"文件是k8s命令文件,用来创建k8s对象。“kubernetes”目录下有两个子目录“backend”和“database”分别存放后端程序和数据库的配置文件。它们内部的结构是类似的,都有三个“yaml”文件:
backend-deployment.yaml:部署配置文件,
backend-service.yaml:服务配置文件
backend-volume.yaml:持久卷配置文件.
关于k8s的核心概念,请参阅“通过实例快速掌握k8s(Kubernetes)核心概念”. “backend”目录还多了一个“docker”子目录用来存储backend应用的Docker镜像,database的镜像文件直接从Docker的库中取得,因此不需要另外生成镜像文件。
k8s参数配置:
要想集成应用程序和k8s需要两个层面的参数共享,一个是应用程序和k8s之间的参数共享,另一个是不同k8s服务之间的参数共享。
k8s共享参数定义:
共享参数可以通过两种方式实现,一个是环境变量,另一个是持久卷。这两种方式大同小异,我们这里用环境变量的方式。这其中最关键的是“k8sdemo-config.yaml”和"k8sdemo-secret.yaml"这两个文件,它们分别存储了普通参数和保密参数。这些参数是属于整个应用程序的,被各个服务共享。
下面就是“k8sdemo-config.yaml”,它里面(在“data:”下面)定义了三个数据库参数,分别是数据库主机(MYSQL_HOST),数据库端口(MYSQL_PORT),数据库名(MYSQL_DATABASE)。
apiVersion: v1
kind: ConfigMap
metadata:
name: k8sdemo-config # ConfigMap的名字, 在引用数据时需要
labels:
app: k8sdemo
data:
MYSQL_HOST: k8sdemo-database-service # 数据库主机
MYSQL_PORT: "3306" # 数据库端口
MYSQL_DATABASE: service_config # 数据库名
下面就是“k8sdemo-secret.yaml”,它里面(在“data:”下面)也定义了三个数据库参数,根用户口令(MYSQL_ROOT_PASSWORD),普通用户名(MYSQL_USER_NAME),普通用户口令(MYSQL_USER_PQSSWORD)
apiVersion: v1
kind: Secret
metadata:
name: k8sdemo-secret
labels:
app: k8sdemo
data:
MYSQL_ROOT_PASSWORD: cm9vdA== # 根用户口令("root")
MYSQL_USER_NAME: ZGJ1c2Vy # 普通用户名("dbuser")
MYSQL_USER_PASSWORD: ZGJ1c2Vy # 普通用户口令("dbuser")
有关k8s的参数配置详细信息,请参阅“通过搭建MySQL掌握k8s(Kubernetes)重要概念(下):参数配置”.
引用k8s共享参数:
下面就是“backend-deployment.yaml”,它定义了“backend“服务的部署(Deployment)配置。它的“containers:”部分定义了容器,“env:”部分定义了环境变量,也就是我们所熟悉的操作系统的环境变量,一般是由系统来定义。不同的系统例如