本篇文章适合k8s入门参考,使用 yaml 文件和 kubectl 命令完成应用部署。本文的脚本只演示了最基础的配置。
前提条件
- 已经部署好 K8S 集群;
- 本地安装了docker, kubectl;
- kubectl 可以顺利访问集群;
Step 1: 使用 docker 打包镜像
1 编写 Dockerfile 文件。
示例如下:
# 从官方的node镜像开始 FROM node:13-alpine # 创建目录 /app RUN mkdir /app # 设置工作目录 WORKDIR /app # 在 /app 内安装 mysql 依赖 RUN npm i mysql
上面的例子没有加入业务代码,并且没有入口文件,原因将在下文解释。
2 打包镜像
使用如下的命令将第一步的文件进行打包:
# 如果上一步的文件名字为 Dockerfile 并且执行命令的位置在同一个目录。 docker build -t your-name/node-base:1.0 . # 如果文件名不是 Dockerfile 或者命令执行位置不同。 docker build -t your-name/node-base:1.0 -f dockerfilePath .
上面的命令中:
-t node-base:1.0
? 表示给这个镜像打标签,这个是为下一步推送准备的,私有docker 镜像服务器一般会提供完整域名作为前缀,如腾讯云的 TCR 表示为:-t ccr.ccs.tencentyun.com/your-name/node-base:1.0
。-f dockerfilePath
?表示docker file 的具体位置。- 别忘记后面有一个 点
.
。
3 推送镜像到远程服务器
命令如下:
docker push your-name/node-base:1.0 # 或者 docker push ccr.ccs.tencentyun.com/your-name/node-base:1.0
push 之前需要 login,请参考提供商的文档进行即可。
4 镜像分层
在第一步中,你会发现 build 过程里最慢的命令是 RUN npm i mysql
?,这个步骤是从远程服务器拉取依赖包到镜像中,一般的官方服务器都在国外,有些依赖包甚至会拉取失败。所以为了减少每次等待时间,我们会将第一步产生的镜像作为基础镜像。
下面,我们为业务代码打包镜像,Dockerfile 如下:
FROM ccr.ccs.tencentyun.com/your-name/node-base:1.0 COPY . /app WORKDIR /app # 入口文件。在 k8s 中,建议把入口配置在 k8s 的配置中。 # ENTRYPOINT [ "node", "index.js" ]
这一步仅拷贝了本地业务代码,build 的速度非常快。(请别忘记添加 gitignore 或者 gitignore 文件排除非必要文件)。
docker build -t ccr.ccs.tencentyun.com/your-name/biz:1.0 .
5 本地测试
使用 docker images
? 命令可以看到刚刚 build 的镜像全部都在本地。想要直接运行镜像测试一下可以使用下面的命令:
docker run --rm -it ccr.ccs.tencentyun.com/your-name/biz:1.0 sh # 或者这样:(下面的 7af6c817aa0a 是使用 docker images 命令看到的镜像 id) docker run --rm -it 7af6c817aa0a sh
上面的命令中:
--rm
?表示退出后删除这个容器实例。-it
?表示进入交互模式(分别是 -i, -t 两个参数)。sh
?表示进入容器后执行的命令。
具体可以查看 docker run --help
?
执行上述命令就可以直接运行你的应用并进入到了 交互界面。这时候你可以在里面随便造了。
测试没问题后,可以上传你的镜像。
Step 2: 部署到 K8S
下面我们就要开始操作 k8s 集群了。
1 创建 namespace
为了方便后期的管理,一般不建议将业务镜像部署到 default 命名空间。使用如下的命令创建命名空间:
kubectl create namespace your-biz-ns
相对于命令式,k8s 更加推崇的是声明式运维。上面创建namespace 的操作,可以通过如下的 yaml 文件表示。
假设下面的代码 文件名为 ns.yaml,一般情况下会把这个文件随着代码一起保存在代码仓库中。
apiVersion: v1 kind: Namespace metadata: name: your-biz-ns spec: finalizers: - kubernetes
将上面的 ns.yaml 文件执行一下:
kubectl apply -f ns.yaml
kubectl apply
?命令可以完成创建或者修改的动作。
2 部署应用
同样,我们需要编写一个 yaml 来“声明”应用的部署状态。(文件名为:app.yaml)
apiVersion: apps/v1 kind: Deployment metadata: name: biz-deployment namespace: your-biz-ns labels: app: biz spec: replicas: 2 selector: matchLabels: app: biz template: metadata: labels: app: biz spec: containers: - image: ccr.ccs.tencentyun.com/your-name/biz:1.0 imagePullPolicy: IfNotPresent name: biz args: - node - index.js ports: - containerPort: 7304
上面的例子中:
metadata.name
?表示这个部署的名字为 biz-deployment,这个名字你自己取。下面的 namespace 表示我们要把应用部署到 your-biz-ns 这个命名空间。spec.replicas
表示这个部署需要创建 2 个副本(实例)。spec.selector
?表示这个 Deployment 如何找到要管理的的 pods。如果要找到biz应用,只需要找到标签里有 app=biz 的 pods 即可。所以在这个文件中,spec.selector
?和spec.template.metadata
?中的标签需要匹配上。template.spec
?里是具体的Pod 配置。containers[].image
?是镜像地址,imagePullPolicy: IfNotPresent
?表示如果当前镜像不在宿主机上,就取镜像仓库拉。containers[].args
是容器的入口命令,k8s 允许重新指定容器的入口。实际上我们可以在这个文件里配置一些变量,并可以为入口指定动态参数。ports[].containerPort
是你的应用实际上启动的端口(容器内的端口)。
上述文件定义了我们希望应用达到的状态:需要启动 2 个实例,标签是 app=biz,镜像使用 your-name/biz:1.0,端口在 7304 等。k8s 会在接收到这个文件后,将集群的应用调整到我们希望的状态。
将这个配置告诉 k8s:
kubectl apply -f app.yaml
完成之后,通过下面的命令我们可以查看刚刚的部署:
# 查看命名空间 kubectl get namespaces # 或者 kubectl get namespace # 或者 kubectl get ns # k8s 的关键字基本都可以简写,简写列表请查阅官方文档。 # 查看部署。-n 指定了命名空间 kubectl get deploy -n your-biz-ns # 查看 pod。这里没写错,-n 可以后面没有空格 kubectl get po -nyour-biz-ns
3 使用 Service 暴露应用
在 k8s 里,可以使用 service 暴露服务,一个 LoadBalancer 的 service 示例如下:
apiVersion: v1 kind: Service metadata: name: biz-service namespace: your-biz-ns spec: type: LoadBalancer selector: app: biz ports: - protocol: TCP port: 80 targetPort: 7304
上面的配置中,我们将标记为 app=biz 的 pod 的端口 7304 映射到负载均衡的 80 端口。
使用 kubectl apply 应用一下。
查看刚刚创建的 service (svc 是 service 的简写):
kubectl get svc -n your-biz-ns
可以看到 EXTERNAL-IP?信息,这个 ip 就是对外的 ip,现在可以在浏览器中直接访问这个 ip 了。
4 创建 Ingress
在更通用的情况下,我们需要通过域名或路径来暴露并路由服务,此时可以使用 Ingress 配合内网的 service 来暴露服务。使用这种方式,则无需创建 上面步骤的 LoadBalancer 的 service。
创建一个内网 Service:
apiVersion: v1 kind: Service metadata: name: biz-service-inner namespace: your-biz-ns spec: selector: app: biz ports: - protocol: TCP port: 80 targetPort: 7304
上面的配置删除了 type: LoadBalancer
,默认的 Service 使用的是 ClusterIp 模式,这种 Service 只能集群内部的 Pod 中访问。
创建 Ingress:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-corp-ingress namespace: your-biz-ns annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: "www.mycorp.com" http: paths: - path: /biz pathType: Prefix backend: service: name: biz-service-inner port: number: 80
上述 Ingress 配置将域名“www.mycorp.com” 并且路径是 /biz 开头的流量导向了 biz-service-inner这个服务。
请注意:这个配置是 k8s 官方配置,各厂商对于 Ingress 的实现方式不一致,此处的配置需要参考提供商的文档进行 修改。
如在 nginx-ingress 的配置类似如下:
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: "your-nginx-ingress-instance-id" name: nginx-ingress namespace: your-biz-ns spec: rules: - http: paths: - backend: serviceName: biz-service-inner servicePort: 80
精进
到这里,你已经把你到应用部署到了 k8s 集群中。但 k8s 的能力远远不止于此。下面的一些议题请继续研究:
- 坑:本文未给私有镜像配置密钥,拉取镜像会失败,请自行配置。
- 为容器限定运行资源,指定容器的运行需要的CPU和内存(关键词:requests, limits)。
- 给容器配置探测器,让你的应用真正保活(关键词:livenessProbe, readinessProbe)。
- 使用 k8s 管理配置文件(关键词:ConfigMap)。
- k8s 存储挂载(关键词:volumes, volumeMounts, PV, PVC)。
- 如何在集群内隔离容器(关键词:NetworkPolicy)。
- 如何控制某些节点只能部署或者不能部署某些应用(关键词:污点 taint,容忍 tolerations,亲和 affinity)。
- 为集群配置自动水平伸缩(关键词 HPA)。
- 其他类型的工作负载(StatefulSets, DaemonSet, Job 等)。
- 如何监控集群,如何收集系统和应用日志?
- 如何在集群内部进行东西向调用,并监测调用过程,并操控流量(关键词:istio, service mesh)。
- 如何结合 k8s 创建一个健全的 DevOps 体系?
- ...