部署应用到 k8s 入门教程

2021-05-08 17:03:32 浏览数 (1)

本篇文章适合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 的镜像全部都在本地。想要直接运行镜像测试一下可以使用下面的命令:

代码语言:txt复制
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 体系?
  • ...

0 人点赞