提示:本节课最终代码为:feature/s29。
上一节课,我介绍了业界最常见的云原生部署方案。这里,我们通过实战来加深理解。采用云原生化的方式来部署 miniblog 服务,需要部署很多组件,例如:监控告警、日志、miniblog 服务实例等,部署很复杂。
本课程的核心目标是教你如何开发一个优秀的 Go 应用,并不是一个云原生实战。另外,本节课的目的是通过实战,带你进入云原生世界,起到一个敲门砖的作用。所以,本节课只会给你展示云原生世界中最核心的内容:部署 Docker、部署 Kubernetes 集群、在 Kubernetes 集群中部署 miniblog 服务。
安装 Docker
Kubernetes 底层需要一个容器运行时来部署 Pod。所以,首先我们需要部署一个容器运行时,这里我们使用 containerd
作为 Kubernetes 的容器运行时。
安装步骤分为以下几步:
需要确保 CentOS 系统启用了centos-extras
yum 源,默认情况下已经启用,检查方式如下:
$ cat /etc/yum.repos.d/CentOS-Extras.repo
# Qcloud-Extras.repo
[extras]
name=Qcloud-$releasever - Extras
baseurl=http://mirrors.tencentyun.com/centos/$releasever/extras/$basearch/os/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Qcloud-8
Docker 官方文档 Install Docker Engine on CentOS 提供了 3 种安装方法:
通过 Yum 源安装;
通过 RPM 包安装;
通过脚本安装。
这里,我们选择最简单的安装方式:通过 Yum 源安装。它具体又分为下面 3 个步骤。
1)安装 docker。
命令如下:
$ sudo yum install -y yum-utils # 1. 安装 `yum-utils` 包,该包提供了 `yum-config-manager` 工具
$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 2. 安装 `docker-ce.repo` yum 源
$ sudo yum-config-manager --enable docker-ce-nightly docker-ce-test # 3. 启用 `nightly` 和 `test` yum 源
$ sudo yum install -y docker-ce docker-ce-cli containerd.io # 4. 安装最新版本的 docker 引擎和 containerd
2)启动 docker。
可以通过以下命令来启动 docker:
$ sudo systemctl start docker
docker 的配置文件是 /etc/docker/daemon.json
,这个配置文件默认是没有的,需要我们手动创建:
$ sudo tee /etc/docker/daemon.json << EOF
{
"bip": "172.16.0.1/24",
"registry-mirrors": [],
"graph": "/data/lib/docker"
}
EOF
配置参数说明如下:
registry-mirrors
:仓库地址,可以根据需要修改为指定的地址。graph
:镜像、容器的存储路径,默认是/var/lib/docker
。如果你的/
目录存储空间满足不了需求,需要设置graph
为更大的目录。bip
:指定容器的 IP 网段。
配置完成后,需要重启 docker:
$ sudo systemctl restart docker
3)测试 docker 是否安装成功。
$ sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b8dfde127a29: Pull complete
Digest: sha256:0fe98d7debd9049c50b597ef1f85b7c1e8cc81f59c8d623fcb2250e8bec85b38
Status: Downloaded newer image for hello-world:latest
...
Hello from Docker!
This message shows that your installation appears to be working correctly.
....
docker run hello-world
命令会下载 hello-world
镜像,并启动容器,打印安装成功提示信息后退出。
安装成功后,我们还需要做一些其他配置。主要有两个,一个是配置 docker,使其可通过non-root
用户使用;另一个是配置 docker 开机启动。
1)使用non-root
用户操作 docker。
我们在 Linux 系统上操作,为了安全,需要以普通用户的身份登录系统并执行操作。所以,我们需要配置 docker,使它可以被 non-root
用户使用。具体配置方法如下:
$ sudo groupadd docker # 1. 创建 `docker` 用户组
$ sudo usermod -aG docker $USER # 2. 将当前用户添加到 `docker` 用户组下
$ newgrp docker # 3. 重新加载组成员身份
$ docker run hello-world # 4. 确认能够以普通用户使用 docker
如果在执行 sudo groupadd docker
时报 groupadd: group 'docker' already exists
错误,说明 docker
组已经存在了,可以忽略这个报错。
如果你在将用户添加到 docker 组之前,使用 sudo
运行过 docker 命令,你可能会看到以下错误:
WARNING: Error loading config file: /home/user/.docker/config.json -
stat /home/user/.docker/config.json: permission denied
这个错误,我们可以通过删除 ~/.docker/
目录来解决,或者通过以下命令更改 ~/.docker/
目录的所有者和权限:
$ sudo chown "$USER":"$USER" /home/"$USER"/.docker -R
$ sudo chmod g+rwx "$HOME/.docker" -R
2)配置 docker 开机启动。
配置命令如下:
$ sudo systemctl enable docker.service # 设置 docker 开机启动
$ sudo systemctl enable containerd.service # 设置 containerd 开机启动
安装 Kind 集群
Kind (Kubernetes in Docker)是一个工具。可以在本地快速创建、删除 Kubernetes 集群。Kind 是我用过的所有 Kubernetes 集群管理工具中最简单的一个。它的学习成本最低,对用户操作也最友好。
安装完 docker 之后,接下来我们就要部署一个 Kubernetes 集群。最简单的方式是通过 kind 来创建一个 kind 集群。此步骤又分为以下几步:
安装命令如下:
$ curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.17.0/kind-linux-amd64
$ chmod +x ./kind
$ sudo mv ./kind /usr/local/bin/kind
$ kind version # 验证 kind
kind v0.14.0 go1.18.2 linux/amd64
为了访问 Kind 集群,我们需要安装 kubectl
命令,安装命令如下:
$ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
$ chmod +x kubectl
$ sudo mv kubectl /usr/bin/
$ kubectl version --client --output=yaml # 验证 kubectl 是否安装成功
clientVersion:
buildDate: "2022-08-23T17:44:59Z"
compiler: gc
gitCommit: a866cbe2e5bbaa01cfd5e969aa3e033f3282a8a2
gitTreeState: clean
gitVersion: v1.25.0
goVersion: go1.19
major: "1"
minor: "25"
platform: linux/amd64
kustomizeVersion: v4.5.7
安装好 kind
命令后,就可以通过 kind
命令来创建一个 Kubernetes 集群,创建命令如下:
$ kind create cluster --name=miniblog-cluster
Creating cluster "miniblog-cluster" ...
✓ Ensuring node image (kindest/node:v1.24.0) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-miniblog-cluster"
You can now use your cluster with:
kubectl cluster-info --context kind-miniblog-cluster
Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
$ kind get clusters # 查询 Kind 集群列表
miniblog-cluster
上述命令会成功创建一个 Kubernetes 集群,并将 kubectl
命令的 context 设置为新创建的 Kind 集群。
相应的你可以通过 kind delete cluster --name=miniblog-cluster
来删除所创建的集群。
我们通过 kubectl cluster-info
来访问新建的 Kind 集群,以验证集群成功创建。访问命令如下:
$ kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:37949
CoreDNS is running at https://127.0.0.1:37949/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
部署 miniblog
上面,我们安装了 Docker,并使用 kind
部署了一个 Kubernetes 集群。接下来,我们就可以在 Kubernetes 集群中部署 miniblog 应用。部署分为以下 2 步:
- 修改配置;
- 部署 miniblog 组件;
- 测试 miniblog 服务是否部署成功。
修改配置
- 创建一个可以通过任意机器访问
miniblog
数据库的用户。
我们需要在容器中访问本地的数据库,所以需要创建一个可以通过任意机器访问的数据库用户,以便从容器中访问数据库,创建命令如下:
$ mysql -uroot -p'miniblog1234'
MariaDB [(none)]> grant all on miniblog.* TO 'docker' @ '%' identified by 'miniblog1234' ;
Query OK, 0 rows affected (0.002 sec)
MariaDB [(none)]> flush privileges;
Query OK, 0 rows affected (0.001 sec)
- 修改
miniblog.yaml
。
修改 configs/miniblog.yaml
文件,更改其中的:tls.cert
、tls.key
、db.host
、db.username
、db.password
配置。
将 tls.cert
、tls.key
分别设置为:/etc/miniblog/cert/server.crt
和 /etc/miniblog/cert/server.key
。
将 db.username
、db.password
设置为第 1 步中设置的用户名和密码。db.host
设置为数据库所在的真实 IP 地址(例如:外网 IP 地址)。
修改后的 miniblog.yaml
文件内容为:
# 通用配置
runmode: debug # Gin 开发模式, 可选值有:debug, release, test
addr: :8080 # HTTP 服务器监听地址
jwt-secret: Rtg8BPKNEf2mB4mgvKONGPZZQSaJWNLijxR42qRgq0iBb5 # JWT 签发密钥
# HTTPS 服务器相关配置
tls:
addr: :8443 # HTTPS 服务器监听地址
cert: /etc/miniblog/cert/server.crt # 证书
key: /etc/miniblog/cert/server.key # 证书 Key 文件
# GRPC 相关配置
grpc:
addr: :9090 # GRPC 服务器监听地址
# MySQL 数据库相关配置
db:
host: xx.xx.xx.xx # 注意替换这里的 IP 为实际的容器中可访问的 IP 地址
username: docker # MySQL 用户名(建议授权最小权限集)
password: miniblog1234 # MySQL 用户密码
database: miniblog # miniblog 系统所用的数据库名
max-idle-connections: 100 # MySQL 最大空闲连接数,默认 100
max-open-connections: 100 # MySQL 最大打开的连接数,默认 100
max-connection-life-time: 10s # 空闲连接最大存活时间,默认 10s
log-level: 4 # GORM log level, 1: silent, 2:error, 3:warn, 4:info
# 日志配置
log:
disable-caller: false # 是否开启 caller,如果开启会在日志中显示调用日志所在的文件和行号
disable-stacktrace: false # 是否禁止在 panic 及以上级别打印堆栈信息
level: debug # 指定日志级别,可选值:debug, info, warn, error, dpanic, panic, fatal
format: console # 指定日志显示格式,可选值:console, json
output-paths: [/tmp/miniblog.log, stdout] # 指定日志输出位置,多个输出,用 `逗号 + 空格` 分开。stdout:标准输出,
部署 miniblog 组件
接下来,我们就可以部署 miniblog 相关组件,需要部署以下 Kubernetes 资源。
ConfigMap:需要部署 2 个 ConfigMap。
- miniblog:miniblog 服务配置。
- miniblog-cert:miniblog 服务需要加载的 CA 文件。
Deployment:通过 Deployment 来部署 miniblog 服务。副本数为 2,实现高可用。
Service:用来通过 Kubernetes Service 访问 miniblog 服务。
接下来,根据顺序,我们依次部署需要的 Kubernetes 文件。
- 制作 Docker image
执行以下命令构建 Docker image:
$ make image
miniblog 的 Dockerfile 文件为:./build/docker/miniblog/Dockerfile
。
- 部署 ConfigMap。
先生成 CA 证书,命令如下:
$ make ca
生成的 CA 证书文件保存在 _output/cert
目录下。
创建 miniblog
和 miniblog-cert
ConfigMap,创建命令如下:
$ kubectl create configmap miniblog --from-file=configs/miniblog.yaml
$ kubectl create configmap miniblog-cert --from-file=./_output/cert
$ kubectl get cm | grep miniblog
miniblog 1 81s
miniblog-cert 8 76s
- Deployment 。
部署命令如下:
$ kubectl create -f deployments/miniblog-deployment.yaml
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
miniblog-695c787f9f-s8sg7 0/1 Running 0 9s
miniblog-695c787f9f-zmdwf 0/1 Running 0 9s
可以看到 miniblog Deployment 成功创建了 2 个 POD:miniblog-695c787f9f-s8sg7
和 miniblog-695c787f9f-zmdwf
。
- 部署 Service。
$ kubectl create -f deployments/miniblog-service.yaml
$ kubectl get svc |grep miniblog
miniblog ClusterIP 10.96.235.74 <none> 8443/TCP,8080/TCP,9090/TCP 26s
可以看到,我们成功创建了一个 miniblog
服务,其 ClusterIP 为 10.96.235.74
。
测试 miniblog 服务是否部署成功
上面,我们成功部署了 miniblog 服务,这里我们需要测试下服务是否正常工作。
通过执行 kubectl get pods
获取 miniblog 的部署实例:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
miniblog-695c787f9f-s8sg7 1/1 Running 0 3m17s
miniblog-695c787f9f-zmdwf 1/1 Running 0 3m17s
登录到其中一个实例上,请求 miniblog 的 /healthz
接口:
$ kubectl exec -it miniblog-695c787f9f-s8sg7 -- bash
[root@miniblog-695c787f9f-s8sg7 miniblog]# curl http://10.96.235.74:8080/healthz
{"status":"ok"}
请求 /healthz
接口,成功返回 {"status":"ok"}
说明服务正常工作。
这里需要注意,我们需要通过 miniblog
服务 IP 10.96.235.74
访问 miniblog 的后端实例。通过 Kubernetes Service 访问 miniblog
服务,可以将请求动态负载到后端的部署实例上。
小结
本节课带你一步一步在 kind 集群中成功部署了 miniblog 服务。本节课旨在通过云原生世界中最核心的操作实战,带你进入云原生世界。根据需要自行学习云原生相关知识,以提高自己的研发能力。