云原生时代,拥抱微服务(二)—— 容器化应用

前文我们已经搭建好k8s集群,接下来就可以对部分项目进行容器化改造了。

“万丈高楼平地起,敲好代码是关键。” —— 皮皮叨叨

创建镜像文件Dockerfile

以Node.js应用为例:

FROM node:12-alpine

# install tzdata so that we can set timezone which default is UTC
RUN apk add tzdata
ENV TZ Asia/Shanghai

# set our node environment, either development or production
# defaults to production, can override this to development on build and run
ARG NODE_ENV=production
ENV NODE_ENV $NODE_ENV

# default to port 3000 for node, and 9229 and 9230 (tests) for debug
ARG PORT=3000
ENV PORT $PORT
EXPOSE $PORT 9229 9230

# install dependencies first, in a different location for easier app bind mounting for local development
# due to default /opt permissions we have to create the dir with root and change perms
RUN mkdir /your-app && chown node:node /your-app
RUN mkdir /var/log/your-app && chown node:node /var/log/your-app
WORKDIR /your-app
# the official node image provides an unprivileged user as a security best practice
# but we have to manually enable it. We put it here so yarn installs dependencies as the same
# user who runs the app.
# https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md#non-root-user
USER node
COPY --chown=node:node . .
RUN yarn install --production=true
ENV PATH /your-app/node_modules/.bin:$PATH

CMD [ "yarn", "start" ]

PS: .dockerignore文件可通过gitignore.io生成。

应用配置信息及密钥管理

普通配置信息

ConfigMaps是一种Kubernetes资源,允许你为容器保存配置信息,这些配置后续可以通过挂载为容器的环境变量或者文件进行访问。

# your-config.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: your-api
data:
  mongodb_host: your.mongodb.host
  mongodb_database: your-database-name

通过在k8s集群中使用kubectl apply命令创建上述命名为your-apiConfigMap资源(如果该资源已经存在,则进行更新动作)。

kubectl apply -f path-to/your-config

密钥等敏感信息

不像ConfigMapsSecrets是一种用于存放敏感信息的Kubernetes资源,一般可用于存放各类凭证、ssh密钥,TLS密钥及证书,或者其他在应用中需要使用的敏感数据。Secrets同样可以挂载为容器的环境变量或者文件。

Secrets被保存为base64编码的字符串,我们需要在Secrets声明文件中将对应敏感信息(如数据库用户名与密码)转换为base64格式。可使用如下命令:

echo -n "db-user-name" | base64
# output: ZGItdXNlci1uYW1l

echo -n "db-pwd" | base64
# output: ZGItcHdk

注意,上述命令输出后的base64编码字符串就是我们需要保持到Kubernetes中的Secrets信息。

# your-secrets.yaml
---
apiVersion: v1
kind: Secret
metadata:
  name: your-api
data:
  mongodb_username: ZGItdXNlci1uYW1l
  mongodb_password: ZGItcHdk

使用kubectl apply命令创建对应Secrets资源:

kubectl apply -f path-to/your-secrets.yaml

【温馨提示】base64编码的字符串并不是被加密的,需要将上述Secrets声明文件保存到足够安全的地方,并且防止泄露!!!建议在Kubernetes集群中统一进行管理。

为应用程序创建Deployment工作负载

工作负载(Workloads)资源可以理解为在Kubernetes中的应用程序,主要包括:DeploymentStatefulSetDaemonSetJobCronJob等类型。

Deployment主要用于扩展与更新无状态容器Pod,适用于我们无状态的应用程序。Deployment会自动创建副本集(ReplicaSet)资源,以用于替换失败的Pods,弹性扩展或减少Pods

# your-deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: your-api
  labels:
    app: your-api-label
spec:
  replicas: 3
  selector:
    matchLabels:
      app: your-api-label
  template:
    metadata:
      labels:
        app: your-api-label
    spec:
      containers:
        - name: your-api
          image: your-api-image:1.0.0
          env:
            - name: MONGODB_HOST
              valueFrom:
                configMapKeyRef:
                  name: your-api
                  key: mongodb_host
            - name: MONGODB_DATABASE
              valueFrom:
                configMapKeyRef:
                  name: your-api
                  key: mongodb_database
            - name: MONGODB_USER
              valueFrom:
                secretKeyRef:
                  name: your-api
                  key: mongodb_username
            - name: MONGODB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: your-api
                  key: mongodb_password

使用kubectl apply命令创建对应Deployment资源:

kubectl apply -f path-to/your-depoyment.yaml

使用Service公开Deployment工作负载

我们还需要通过创建Service资源以更好地访问不同Pod中的应用程序,并且能做到负载均衡。这主要是由于Pod是短暂的,一旦由于某种原因被销毁,其对应的IP地址也会被吊销,然后新生的Pod将获得一个不同的IP地址。

# your-service.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: your-api
spec:
  type: ClusterIP
  selector:
    app: your-api-label
  ports:
    - protocol: TCP
      port: 80
      targetPort: http

使用kubectl apply命令创建对应Service资源:

kubectl apply -f path-to/your-service.yaml

总结

我们首先通过创建Dockerfile以用于生成应用程序的容器镜像,然后通过使用ConfigMapsSecrets资源管理应用程序的配置及敏感信息,最后再运用DeploymentService资源部署及公开应用程序。

微服务高楼的基石貌似已经搭好了,胜利的曙光就在眼前。但是机智且追求效率的你一定发现了:一个应用程序这样做还好,可如果有多个应用程序也需要容器化,都分别去声明各种Kubernetes资源岂不是很繁琐?你这个ClusterIP类型的Service似乎还无法从外网访问?

云原生时代,拥抱微服务(一) —— 搭建k8s集群

背景

现有系统是以传统的单片形式进行架构,通过采用Nginx对后端项目及前端进行反向代理。基础设施使用的是阿里云ECS服务器,直接在ECS中运行项目。在项目初期这种方式可以较快的搭建起应用,但随着项目不断地迭代,整个后端系统变得越来越臃肿,且难以进行弹性扩展和维护。为了让日后的开发及运维更为平顺,微服务化势在必行。

时间就是生命,提升效率就是延长生命。 —— 皮皮叨叨

既然有了这个想法,下面就要一步步落实了。微服务化第一步就是要将项目进行容器化,而为了更好地管理容器就需要一套能“用于自动部署、扩展和管理容器化应用程序的平台”,我们将采用业界普遍使用的Kubernetes作为容器管理平台。

云平台K8S or 自行搭建?

我们最终选择自行搭建K8S平台,主要基于下述原因:

  • 已经购买了包年的ECS套餐
  • 基于费用成本考虑,阿里云容器服务Kubernetes版计费更为复杂(除了集群管理费,还包括云服务器、NAT网关、负载均衡等相关费用),总体而言费用相对更高。
  • 需要平稳过渡到新的部署架构,先从后端项目入手,成熟后再将前端等其他项目迁移过去。
  • 如果后续有需要的话,自建K8S也可以很快的迁移到使用云平台K8S方案(通过配置文件对Kubernetes对象进行声明式管理)

我们将使用轻量的、产品级且易于运维的microk8s搭建K8S集群。

搭建microk8s集群(基于Ubuntu 18.04)

  1. 安装snappy包管理器(若已安装,请忽略)
sudo apt update
sudo apt install snapd
  1. 安装microk8s(以1.17版本为例)
sudo snap install microk8s --classic --channel=1.17/stable

## 配置防火墙以允许pod间通信
sudo ufw allow in on cni0 && sudo ufw allow out on cni0

## 创建kubectl别名以方便后续使用
echo "alias kubectl='microk8s.kubectl'" >> ~/.bashrc
source ~/.bashrc
## 或者
sudo snap alias microk8s.kubectl kubectl
  1. 开启CoreDNS以支持k8s地址解析服务(推荐启用)
microk8s.enable dns 

## 查看microk8s状态
microk8s.status

可以看到microk8s已成功运行且dns插件已启用

## 检查coreDNS运行情况
kubectl get pods --all-namespaces

相关pod处于containerCreateing状态

发现相关pod一直处于ContainerCreating状态!!!

## 继续查找原因
kubectl describe pod/coredns-9b8997588-dp9cx -n kube-system

发现由于某种原因 *无法获取* 指定镜像 **k8s.gcr.io/pause:3.1**

发现由于某种原因 无法获取 指定镜像 k8s.gcr.io/pause:3.1解决方法如下:

## 下载docker hub 上的相关镜像
sudo docker pull mirrorgooglecontainers/pause:3.1 
## 将相关镜像tag为 k8s.gcr.io/pause:3.1
sudo docker tag mirrorgooglecontainers/pause:3.1 k8s.gcr.io/pause:3.1
## 保存镜像为本地镜像文件
sudo docker save k8s.gcr.io/pause > pause.tar
## ctr导入本地镜像
sudo microk8s.ctr image import pause.tar
  1. 增加microk8s节点
## 主节点运行命令
microk8s add-node

上述命令打印出microk8s join指令,用于在其他需要加入集群的节点执行。PS:需要确保节点间网络互通(并在安全组及主机防火墙开放),否则加入不成功!!!

## 查看k8s集群节点信息
kubectl get nodes


大功告成!!!接下来可以愉快地对项目进行容器化管理了^-^