573 lines
19 KiB
Markdown
573 lines
19 KiB
Markdown
|
||
|
||
因收到Google相关通知,网站将会择期关闭。相关通知内容
|
||
|
||
|
||
09 应用发布:部署实际项目
|
||
本节我们开始学习如何将实际项目部署至 K8S 中,开启生产实践之路。
|
||
|
||
整体概览
|
||
|
||
本节所用示例项目是一个混合了 Go,NodeJS,Python 等语言的项目,灵感来自于知名程序员 Kenneth Reitz 的 Say Thanks 项目。本项目实现的功能主要有两个:1. 用户通过前端发送感谢消息 2. 有个工作进程会持续的计算收到感谢消息的排行榜。项目代码可在 GitHub 上获得。接下来几节中如果需要用到此项目我会统一称之为 saythx 项目。
|
||
|
||
saythx 项目的基础结构如下图:
|
||
|
||
|
||
|
||
构建镜像
|
||
|
||
前端
|
||
|
||
我们使用了前端框架 Vue,所以在做生产部署时,需要先在 Node JS 的环境下进行打包构建。包管理器使用的是 Yarn。然后使用 Nginx 提供服务,并进行反向代理,将请求正确的代理至后端。
|
||
|
||
FROM node:10.13 as builder
|
||
|
||
WORKDIR /app
|
||
COPY . /app
|
||
|
||
RUN yarn install \
|
||
&& yarn build
|
||
|
||
FROM nginx:1.15
|
||
|
||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||
COPY --from=builder /app/dist /usr/share/nginx/html/
|
||
|
||
EXPOSE 80
|
||
|
||
|
||
Nginx 的配置文件如下:
|
||
|
||
upstream backend-up {
|
||
server saythx-backend:8080;
|
||
}
|
||
server {
|
||
listen 80;
|
||
server_name localhost;
|
||
|
||
charset utf-8;
|
||
|
||
location / {
|
||
root /usr/share/nginx/html;
|
||
try_files $uri $uri/ /index.html;
|
||
}
|
||
|
||
location ~ ^/(api) {
|
||
proxy_pass http://backend-up;
|
||
}
|
||
}
|
||
|
||
|
||
将 API 的请求反向代理到后端服务上。其余请求全部留给前端进行处理。
|
||
|
||
后端
|
||
|
||
后端是使用 Golang 编写的 API 服务,对请求进行相应处理,并将数据存储至 Redis 当中。依赖管理使用的是 dep。由于 Golang 是编译型语言,编译完成后会生成一个二进制文件,为了让镜像尽可能小,所以 Dockerfile 和前端的差不多,都使用了多阶段构建的特性。
|
||
|
||
FROM golang:1.11.1 as builder
|
||
|
||
WORKDIR /go/src/be
|
||
COPY . /go/src/be
|
||
RUN go get -u github.com/golang/dep/cmd/dep \
|
||
&& dep ensure \
|
||
&& go build
|
||
|
||
FROM debian:stretch-slim
|
||
COPY --from=builder /go/src/be/be /usr/bin/be
|
||
ENTRYPOINT ["/usr/bin/be"]
|
||
EXPOSE 8080
|
||
|
||
|
||
注意这里会暴露出来后端服务所监听的端口。
|
||
|
||
Work
|
||
|
||
Work 端使用的是 Python,用于计算已经存储至 Redis 当中的数据,并生成排行榜。依赖使用 pip 进行安装。对于 Python 的镜像选择,我做了一组性能对比的测试 有兴趣可以了解下。
|
||
|
||
FROM python:3.7-slim
|
||
|
||
WORKDIR /app
|
||
COPY . /app
|
||
RUN pip install -r requirements.txt
|
||
|
||
ENTRYPOINT ["python", "work.py"]
|
||
|
||
|
||
构建发布
|
||
|
||
接下来,我们只要在对应项目目录中,执行 docker build [OPTIONS] PATH 即可。一般我们会使用 -t name:tag 的方式打 tag。
|
||
|
||
# 以下分别是在各模块自己的目录内
|
||
➜ be docker build -t taobeier/saythx-be .
|
||
➜ fe docker build -t taobeier/saythx-fe .
|
||
➜ work docker build -t taobeier/saythx-work .
|
||
|
||
|
||
需要注意的是,前端项目由于目录内包含开发时的 node_modules 等文件,需要注意添加 .dockerignore 文件,忽略一些非预期的文件。关于 Docker 的 build 原理,有想深入理解的,可参考我之前写的 Docker 深入篇之 Build 原理 。 当镜像构建完成后,我们需要将它们发布至镜像仓库。这里我们直接使用官方的 Docker Hub,执行 docker login 输入用户名密码验证成功后便可进行发布(需要先去 Docker Hub 注册帐号)。
|
||
|
||
登录成功后,默认情况下在 $HOME/.docker/config.json 中会存储用户相关凭证。
|
||
|
||
接下来进行发布只需要执行 docker push 即可。
|
||
|
||
➜ ~ docker push taobeier/saythx-be
|
||
➜ ~ docker push taobeier/saythx-fe
|
||
➜ ~ docker push taobeier/saythx-work
|
||
|
||
|
||
容器编排 Docker Compose
|
||
|
||
Docker Compose 是一种较为简单的可进行容器编排的技术,需要创建一个配置文件,通常情况下为 docker-compose.yml 。在 saythx 项目的根目录下我已经创建好了 docker-compose.yml 的配置文件。
|
||
|
||
version: '3'
|
||
|
||
services:
|
||
saythx-frontend:
|
||
build:
|
||
context: fe/.
|
||
image: taobeier/saythx-fe
|
||
ports:
|
||
- "8088:80"
|
||
depends_on:
|
||
- saythx-backend
|
||
networks:
|
||
- saythx
|
||
|
||
saythx-backend:
|
||
build:
|
||
context: be/.
|
||
image: taobeier/saythx-be
|
||
depends_on:
|
||
- saythx-redis
|
||
networks:
|
||
- saythx
|
||
environment:
|
||
- REDIS_HOST=saythx-redis
|
||
|
||
saythx-work:
|
||
build:
|
||
context: work/.
|
||
image: taobeier/saythx-work
|
||
depends_on:
|
||
- saythx-redis
|
||
networks:
|
||
- saythx
|
||
environment:
|
||
- REDIS_HOST=saythx-redis
|
||
- REDIS_PORT=6379
|
||
|
||
saythx-redis:
|
||
image: "redis:5"
|
||
networks:
|
||
- saythx
|
||
|
||
networks:
|
||
saythx:
|
||
|
||
|
||
在项目的根目录下执行 docker-compose up 即可启动该项目。在浏览器中访问 http://127.0.0.1:8088/ 即可看到项目的前端界面。如下图:
|
||
|
||
|
||
|
||
打开另外的终端,进入项目根目录内,执行 docker-compose ps 命令即可看到当前的服务情况。
|
||
|
||
➜ saythx git:(master) ✗ docker-compose ps
|
||
|
||
Name Command State Ports
|
||
----------------------------------------------------------------------------------------
|
||
saythx_saythx-backend_1 /usr/bin/be Up 8080/tcp
|
||
saythx_saythx-frontend_1 nginx -g daemon off; Up 0.0.0.0:8088->80/tcp
|
||
saythx_saythx-redis_1 docker-entrypoint.sh redis ... Up 6379/tcp
|
||
saythx_saythx-work_1 python work.py Up
|
||
|
||
|
||
可以看到各组件均是 Up 的状态,相关端口也已经暴露出来。
|
||
|
||
可在浏览器直接访问体验。由于 Docker Compose 并非本册的重点,故不做太多介绍,可参考官方文档进行学习。接下来进入本节的重点内容,将项目部署至 K8S 中。
|
||
|
||
编写配置文件并部署
|
||
|
||
在 K8S 中进行部署或者说与 K8S 交互的方式主要有三种:
|
||
|
||
|
||
命令式
|
||
命令式对象配置
|
||
声明式对象配置
|
||
|
||
|
||
第 7 节介绍过的 kubectl run redis --image='redis:alpine' 这种方式便是命令式,这种方式很简单,但是可重用性低。毕竟你的命令执行完后,其他人也并不清楚到底发生了什么。
|
||
|
||
命令式对象配置,主要是编写配置文件,但是通过类似 kubectl create 之类命令式的方式进行操作。
|
||
|
||
再有一种便是声明式对象配置,主要也是通过编写配置文件,但是使用 kubectl apply 之类的放好似进行操作。与第二种命令式对象配置的区别主要在于对对象的操作将会得到保留。但同时这种方式有时候也并不好进行调试。
|
||
|
||
接下来,为 saythx 项目编写配置文件,让它可以部署至 K8S 中。当然,这里我们已经创建过了 docker-compose.yml 的配置文件,并且也验证了其可用性,可以直接使用 Kompose 工具将 docker-compose.yml 的配置文件进行转换。
|
||
|
||
但这里采用直接编写的方式。同时,我们部署至一个新的名为 work 的 Namespace 中。
|
||
|
||
Namespace
|
||
|
||
apiVersion: v1
|
||
kind: Namespace
|
||
metadata:
|
||
name: work
|
||
|
||
|
||
指定了 Namespace name 为 work。然后进行部署
|
||
|
||
➜ conf git:(master) ✗ kubectl apply -f namespace.yaml
|
||
namespace/work created
|
||
|
||
|
||
Redis 资源
|
||
|
||
从前面的 docker-compose.yml 中也能发现,saythx 中各个组件,只有 Redis 是无任何依赖的。我们先对它进行部署。
|
||
|
||
apiVersion: apps/v1
|
||
kind: Deployment
|
||
metadata:
|
||
labels:
|
||
app: redis
|
||
name: saythx-redis
|
||
namespace: work
|
||
spec:
|
||
selector:
|
||
matchLabels:
|
||
app: redis
|
||
replicas: 1
|
||
template:
|
||
metadata:
|
||
labels:
|
||
app: redis
|
||
spec:
|
||
containers:
|
||
- image: redis:5
|
||
name: redis
|
||
ports:
|
||
- containerPort: 6379
|
||
|
||
|
||
由于这是本册内第一次出现完整的 Deployment 配置文件,故而进行重点介绍。
|
||
|
||
|
||
apiVersion :指定了 API 的版本号,当前我们使用的 K8S 中, Deployment 的版本号为 apps/v1,而在 1.9 之前使用的版本则为 apps/v1beta2,在 1.8 之前的版本使用的版本为 extensions/v1beta1。在编写配置文件时需要格外注意。
|
||
kind :指定了资源的类型。这里指定为 Deployment 说明是一次部署。
|
||
metadata :指定了资源的元信息。例如其中的 name 和 namespace 分别表示资源名称和所归属的 Namespace。
|
||
spec :指定了对资源的配置信息。例如其中的 replicas 指定了副本数当前指定为 1 。template.spec 则指定了 Pod 中容器的配置信息,这里的 Pod 中只部署了一个容器。
|
||
|
||
|
||
配置文件已经生产,现在对它进行部署。
|
||
|
||
➜ conf git:(master) ✗ kubectl -n work get all
|
||
No resources found.
|
||
➜ conf git:(master) ✗ kubectl apply -f redis-deployment.yaml
|
||
deployment.apps/saythx-redis created
|
||
➜ conf git:(master) ✗ kubectl -n work get all
|
||
NAME READY STATUS RESTARTS AGE
|
||
pod/saythx-redis-79d8f9864d-x8fp9 1/1 Running 0 4s
|
||
|
||
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
|
||
deployment.apps/saythx-redis 1 1 1 1 4s
|
||
|
||
NAME DESIRED CURRENT READY AGE
|
||
replicaset.apps/saythx-redis-79d8f9864d 1 1 1 4s
|
||
|
||
|
||
可以看到 Pod 已经在正常运行了。我们进入 Pod 内进行测试。
|
||
|
||
➜ conf git:(master) ✗ kubectl -n work exec -it saythx-redis-79d8f9864d-x8fp9 bash
|
||
root@saythx-redis-79d8f9864d-x8fp9:/data# redis-cli
|
||
127.0.0.1:6379> ping
|
||
PONG
|
||
|
||
|
||
响应正常。
|
||
|
||
Redis service
|
||
|
||
由于 Redis 是后端服务的依赖,我们将它作为 Service 暴露出来。
|
||
|
||
apiVersion: v1
|
||
kind: Service
|
||
metadata:
|
||
labels:
|
||
app: redis
|
||
name: saythx-redis
|
||
namespace: work
|
||
spec:
|
||
ports:
|
||
- protocol: TCP
|
||
port: 6379
|
||
targetPort: 6379
|
||
selector:
|
||
app: redis
|
||
type: NodePort
|
||
|
||
|
||
关于 Service 的内容,可参考第 7 节,我们详细做过解释。这里直接使用配置文件进行部署。
|
||
|
||
➜ conf git:(master) ✗ kubectl apply -f redis-service.yaml
|
||
service/saythx-redis created
|
||
➜ conf git:(master) ✗ kubectl get svc -n work
|
||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||
saythx-redis NodePort 10.107.31.242 <none> 6379:31467/TCP 1m
|
||
|
||
|
||
后端服务
|
||
|
||
接下来,我们对后端服务进行部署。
|
||
|
||
apiVersion: apps/v1
|
||
kind: Deployment
|
||
metadata:
|
||
labels:
|
||
app: backend
|
||
name: saythx-backend
|
||
namespace: work
|
||
spec:
|
||
selector:
|
||
matchLabels:
|
||
app: backend
|
||
replicas: 1
|
||
template:
|
||
metadata:
|
||
labels:
|
||
app: backend
|
||
spec:
|
||
containers:
|
||
- env:
|
||
- name: REDIS_HOST
|
||
value: saythx-redis
|
||
image: taobeier/saythx-be
|
||
name: backend
|
||
ports:
|
||
- containerPort: 8080
|
||
|
||
|
||
可以看到这里通过环境变量的方式,将 REDIS_HOST 传递给了后端服务。
|
||
|
||
➜ conf git:(master) ✗ kubectl apply -f backend-deployment.yaml
|
||
deployment.apps/saythx-backend created
|
||
➜ conf git:(master) ✗ kubectl -n work get all
|
||
NAME READY STATUS RESTARTS AGE
|
||
pod/saythx-backend-c5f9f6d95-lmtxn 0/1 ContainerCreating 0 5s
|
||
pod/saythx-redis-8558c7d7d-kcmzk 1/1 Running 0 17m
|
||
|
||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||
service/saythx-redis NodePort 10.107.31.242 <none> 6379:31467/TCP 1m
|
||
|
||
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
|
||
deployment.apps/saythx-backend 1 1 1 0 5s
|
||
deployment.apps/saythx-redis 1 1 1 1 17m
|
||
|
||
NAME DESIRED CURRENT READY AGE
|
||
replicaset.apps/saythx-backend-c5f9f6d95 1 1 0 5s
|
||
replicaset.apps/saythx-redis-8558c7d7d 1 1 1 17m
|
||
|
||
|
||
后端 Service
|
||
|
||
后端服务是前端项目的依赖,故而我们也将其作为 Service 暴露出来。
|
||
|
||
apiVersion: v1
|
||
kind: Service
|
||
metadata:
|
||
labels:
|
||
app: backend
|
||
name: saythx-backend
|
||
namespace: work
|
||
spec:
|
||
ports:
|
||
- protocol: TCP
|
||
port: 8080
|
||
targetPort: 8080
|
||
selector:
|
||
app: backend
|
||
type: NodePort
|
||
|
||
|
||
通过配置文件进行部署。
|
||
|
||
➜ conf git:(master) ✗ kubectl apply -f backend-service.yaml
|
||
service/saythx-backend created
|
||
➜ conf git:(master) ✗ kubectl get svc -n work
|
||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||
service/saythx-backend NodePort 10.104.0.47 <none> 8080:32051/TCP 8s
|
||
service/saythx-redis NodePort 10.107.31.242 <none> 6379:31467/TCP 3m
|
||
|
||
|
||
我们同样使用 NodePort 将其暴露出来,并在本地进行测试。
|
||
|
||
➜ conf git:(master) ✗ curl http://127.0.0.1:32051/api/v1/list
|
||
{"HonorDatas":null}
|
||
|
||
|
||
服务可正常响应。
|
||
|
||
前端
|
||
|
||
接下来我们编写前端的配置文件。
|
||
|
||
apiVersion: apps/v1
|
||
kind: Deployment
|
||
metadata:
|
||
labels:
|
||
app: frontend
|
||
name: saythx-frontend
|
||
namespace: work
|
||
spec:
|
||
selector:
|
||
matchLabels:
|
||
app: frontend
|
||
template:
|
||
metadata:
|
||
labels:
|
||
app: frontend
|
||
spec:
|
||
containers:
|
||
- image: taobeier/saythx-fe
|
||
name: frontend
|
||
ports:
|
||
- containerPort: 80
|
||
|
||
|
||
需要注意的是,我们必须在后端 Service 暴露出来后才能进行前端的部署,因为前端镜像中 Nginx 的反向代理配置中会去检查后端是否可达。使用配置文件进行部署。
|
||
|
||
➜ conf git:(master) ✗ kubectl apply -f frontend-deployment.yaml
|
||
deployment.apps/saythx-frontend created
|
||
➜ conf git:(master) ✗ kubectl -n work get all
|
||
NAME READY STATUS RESTARTS AGE
|
||
pod/saythx-backend-c5f9f6d95-lmtxn 1/1 Running 0 16m
|
||
pod/saythx-frontend-678d544b86-wp9gr 1/1 Running 0 30s
|
||
pod/saythx-redis-8558c7d7d-kcmzk 1/1 Running 0 34m
|
||
|
||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||
service/saythx-backend NodePort 10.104.0.47 <none> 8080:32051/TCP 15m
|
||
service/saythx-redis NodePort 10.107.31.242 <none> 6379:31467/TCP 18m
|
||
|
||
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
|
||
deployment.apps/saythx-backend 1 1 1 1 16m
|
||
deployment.apps/saythx-frontend 1 1 1 1 30s
|
||
deployment.apps/saythx-redis 1 1 1 1 34m
|
||
|
||
NAME DESIRED CURRENT READY AGE
|
||
replicaset.apps/saythx-backend-c5f9f6d95 1 1 1 16m
|
||
replicaset.apps/saythx-frontend-678d544b86 1 1 1 30s
|
||
replicaset.apps/saythx-redis-8558c7d7d 1 1 1 34m
|
||
|
||
|
||
前端 Service
|
||
|
||
接下来,我们需要让前端可以被直接访问到,同样的需要将它以 Service 的形式暴露出来。
|
||
|
||
apiVersion: v1
|
||
kind: Service
|
||
metadata:
|
||
labels:
|
||
app: frontend
|
||
name: saythx-frontend
|
||
namespace: work
|
||
spec:
|
||
ports:
|
||
- name: "80"
|
||
port: 80
|
||
targetPort: 80
|
||
selector:
|
||
app: frontend
|
||
type: NodePort
|
||
|
||
|
||
创建 Service 。
|
||
|
||
➜ conf git:(master) ✗ kubectl apply -f frontend-service.yaml
|
||
service/saythx-frontend created
|
||
➜ conf git:(master) ✗ kubectl -n work get svc
|
||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||
saythx-backend NodePort 10.104.0.47 <none> 8080:32051/TCP 17m
|
||
saythx-frontend NodePort 10.96.221.71 <none> 80:32682/TCP 11s
|
||
saythx-redis NodePort 10.107.31.242 <none> 6379:31467/TCP 20m
|
||
|
||
|
||
我们可以直接通过 Node 的 32682 端口进行访问。
|
||
|
||
Work
|
||
|
||
最后,是我们的 Work 组件,为它编写配置文件。
|
||
|
||
apiVersion: apps/v1
|
||
kind: Deployment
|
||
metadata:
|
||
labels:
|
||
app: work
|
||
name: saythx-work
|
||
namespace: work
|
||
spec:
|
||
selector:
|
||
matchLabels:
|
||
app: work
|
||
replicas: 1
|
||
template:
|
||
metadata:
|
||
labels:
|
||
app: work
|
||
spec:
|
||
containers:
|
||
- env:
|
||
- name: REDIS_HOST
|
||
value: saythx-redis
|
||
- name: REDIS_PORT
|
||
value: "6379"
|
||
image: taobeier/saythx-work
|
||
name: work
|
||
|
||
|
||
同样的,我们通过环境变量的方式传递了 Redis 相关的配置进去。
|
||
|
||
➜ conf git:(master) ✗ kubectl apply -f work-deployment.yaml
|
||
deployment.apps/saythx-work created
|
||
➜ conf git:(master) ✗ kubectl -n work get all
|
||
NAME READY STATUS RESTARTS AGE
|
||
pod/saythx-backend-c5f9f6d95-lmtxn 1/1 Running 0 22m
|
||
pod/saythx-frontend-678d544b86-wp9gr 1/1 Running 0 5m
|
||
pod/saythx-redis-8558c7d7d-kcmzk 1/1 Running 0 39m
|
||
pod/saythx-work-6b9958dc47-hh9td 0/1 ContainerCreating 0 7s
|
||
|
||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||
service/saythx-backend NodePort 10.104.0.47 <none> 8080:32051/TCP 20m
|
||
service/saythx-frontend NodePort 10.96.221.71 <none> 80:32682/TCP 3m
|
||
service/saythx-redis NodePort 10.107.31.242 <none> 6379:31467/TCP 23m
|
||
|
||
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
|
||
deployment.apps/saythx-backend 1 1 1 1 22m
|
||
deployment.apps/saythx-frontend 1 1 1 1 5m
|
||
deployment.apps/saythx-redis 1 1 1 1 39m
|
||
deployment.apps/saythx-work 1 1 1 0 7s
|
||
|
||
NAME DESIRED CURRENT READY AGE
|
||
replicaset.apps/saythx-backend-c5f9f6d95 1 1 1 22m
|
||
replicaset.apps/saythx-frontend-678d544b86 1 1 1 5m
|
||
replicaset.apps/saythx-redis-8558c7d7d 1 1 1 39m
|
||
replicaset.apps/saythx-work-6b9958dc47 1 1 0 7s
|
||
|
||
|
||
现在均已经部署完成。并且可直接通过 Node 端口进行访问。
|
||
|
||
扩缩容
|
||
|
||
如果我们觉得排行榜生成效率较低,则可通过扩容 Work 来得到解决。具体做法是可修改 work 的 Deployment 配置文件,将 spec.replicas 设置为预期的数值,之后执行 kubectl -f work-deployment.yaml 即可。
|
||
|
||
或者可直接通过命令行进行操作
|
||
|
||
➜ conf git:(master) ✗ kubectl -n work scale --replicas=2 deployment/saythx-work
|
||
|
||
|
||
上面的命令是将 saythx-work 的部署副本数设置为 2 。缩容也差不多是类似的操作。
|
||
|
||
总结
|
||
|
||
通过本节的学习,我们已经学习到了如何将项目实际部署至 K8S 中,可以手动编写配置也可以利用一些工具进行辅助。同时也了解到了如何应对应用的扩缩容。
|
||
|
||
但如果应用需要进行升级的话,则需要去更改配置文件中相关的配置,这个过程会较为繁琐,并且整体项目线上的版本管理也是个问题:比如组件的个人升级,回滚之间如果有依赖的话,会比较麻烦。我们在接下来的两节来学习如何解决这个问题。
|
||
|
||
|
||
|
||
|