本篇文章适合k8s入门参考,使用 yaml 文件和 kubectl 命令完成应用部署。本文的脚本只演示了最基础的配置。
前提条件
- 已经部署好 K8S 集群;
- 本地安装了docker, kubectl;
- kubectl 可以顺利访问集群;
Step 1: 使用 docker 打包镜像
1 编写 Dockerfile 文件。
示例如下:
代码语言:txt复制# 从官方的node镜像开始
FROM node:13-alpine
# 创建目录 /app
RUN mkdir /app
# 设置工作目录
WORKDIR /app
# 在 /app 内安装 mysql 依赖
RUN npm i mysql
上面的例子没有加入业务代码,并且没有入口文件,原因将在下文解释。
2 打包镜像
使用如下的命令将第一步的文件进行打包:
代码语言:txt复制# 如果上一步的文件名字为 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 推送镜像到远程服务器
命令如下:
代码语言:txt复制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 如下:
代码语言:txt复制FROM ccr.ccs.tencentyun.com/your-name/node-base:1.0
COPY . /app
WORKDIR /app
# 入口文件。在 k8s 中,建议把入口配置在 k8s 的配置中。
# ENTRYPOINT [ "node", "index.js" ]
这一步仅拷贝了本地业务代码,build 的速度非常快。(请别忘记添加 gitignore 或者 gitignore 文件排除非必要文件)。
代码语言:txt复制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 命名空间。使用如下的命令创建命名空间:
代码语言:txt复制kubectl create namespace your-biz-ns
相对于命令式,k8s 更加推崇的是声明式运维。上面创建namespace 的操作,可以通过如下的 yaml 文件表示。
假设下面的代码 文件名为 ns.yaml,一般情况下会把这个文件随着代码一起保存在代码仓库中。
代码语言:txt复制apiVersion: v1
kind: Namespace
metadata:
name: your-biz-ns
spec:
finalizers:
- kubernetes
将上面的 ns.yaml 文件执行一下:
代码语言:txt复制kubectl apply -f ns.yaml
kubectl apply
命令可以完成创建或者修改的动作。
2 部署应用
同样,我们需要编写一个 yaml 来“声明”应用的部署状态。(文件名为:app.yaml)
代码语言:txt复制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:
代码语言:txt复制kubectl apply -f app.yaml
完成之后,通过下面的命令我们可以查看刚刚的部署:
代码语言:txt复制# 查看命名空间
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 示例如下:
代码语言:txt复制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 的简写):
代码语言:txt复制kubectl get svc -n your-biz-ns
可以看到 EXTERNAL-IP 信息,这个 ip 就是对外的 ip,现在可以在浏览器中直接访问这个 ip 了。
4 创建 Ingress
在更通用的情况下,我们需要通过域名或路径来暴露并路由服务,此时可以使用 Ingress 配合内网的 service 来暴露服务。使用这种方式,则无需创建 上面步骤的 LoadBalancer 的 service。
创建一个内网 Service:
代码语言:txt复制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:
代码语言:txt复制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 的配置类似如下:
代码语言:txt复制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 体系?
- ...