提示:本节课最终代码为:feature/s29

上一节课,我介绍了业界最常见的云原生部署方案。这里,我们通过实战来加深理解。采用云原生化的方式来部署 miniblog 服务,需要部署很多组件,例如:监控告警、日志、miniblog 服务实例等,部署很复杂。

本课程的核心目标是教你如何开发一个优秀的 Go 应用,并不是一个云原生实战。另外,本节课的目的是通过实战,带你进入云原生世界,起到一个敲门砖的作用。所以,本节课只会给你展示云原生世界中最核心的内容:部署 Docker、部署 Kubernetes 集群、在 Kubernetes 集群中部署 miniblog 服务。

安装 Docker

Kubernetes 底层需要一个容器运行时来部署 Pod。所以,首先我们需要部署一个容器运行时,这里我们使用 containerd 作为 Kubernetes 的容器运行时。

安装步骤分为以下几步:

  1. 安装 docker 前置条件检查;

  2. 安装 docker;

  3. docker 安装后配置。

  4. 安装 docker 前置条件检查。

需要确保 CentOS 系统启用了centos-extras yum 源,默认情况下已经启用,检查方式如下:

  1. $ cat /etc/yum.repos.d/CentOS-Extras.repo
  2. # Qcloud-Extras.repo
  3. [extras]
  4. name=Qcloud-$releasever - Extras
  5. baseurl=http://mirrors.tencentyun.com/centos/$releasever/extras/$basearch/os/
  6. gpgcheck=1
  7. enabled=1
  8. gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Qcloud-8
  1. 安装 docker

Docker 官方文档 Install Docker Engine on CentOS 提供了 3 种安装方法:

  • 通过 Yum 源安装;

  • 通过 RPM 包安装;

  • 通过脚本安装。

这里,我们选择最简单的安装方式:通过 Yum 源安装。它具体又分为下面 3 个步骤。

1)安装 docker。

命令如下:

  1. $ sudo yum install -y yum-utils # 1. 安装 `yum-utils` 包,该包提供了 `yum-config-manager` 工具
  2. $ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 2. 安装 `docker-ce.repo` yum 源
  3. $ sudo yum-config-manager --enable docker-ce-nightly docker-ce-test # 3. 启用 `nightly` 和 `test` yum 源
  4. $ sudo yum install -y docker-ce docker-ce-cli containerd.io # 4. 安装最新版本的 docker 引擎和 containerd

2)启动 docker。

可以通过以下命令来启动 docker:

  1. $ sudo systemctl start docker

docker 的配置文件是 /etc/docker/daemon.json,这个配置文件默认是没有的,需要我们手动创建:

  1. $ sudo tee /etc/docker/daemon.json << EOF
  2. {
  3. "bip": "172.16.0.1/24",
  4. "registry-mirrors": [],
  5. "graph": "/data/lib/docker"
  6. }
  7. EOF

配置参数说明如下:

  • registry-mirrors:仓库地址,可以根据需要修改为指定的地址。

  • graph:镜像、容器的存储路径,默认是 /var/lib/docker。如果你的 / 目录存储空间满足不了需求,需要设置 graph 为更大的目录。

  • bip:指定容器的 IP 网段。

配置完成后,需要重启 docker:

  1. $ sudo systemctl restart docker

3)测试 docker 是否安装成功。

  1. $ sudo docker run hello-world
  2. Unable to find image 'hello-world:latest' locally
  3. latest: Pulling from library/hello-world
  4. b8dfde127a29: Pull complete
  5. Digest: sha256:0fe98d7debd9049c50b597ef1f85b7c1e8cc81f59c8d623fcb2250e8bec85b38
  6. Status: Downloaded newer image for hello-world:latest
  7. ...
  8. Hello from Docker!
  9. This message shows that your installation appears to be working correctly.
  10. ....

docker run hello-world 命令会下载 hello-world 镜像,并启动容器,打印安装成功提示信息后退出。

  1. docker 安装后配置

安装成功后,我们还需要做一些其他配置。主要有两个,一个是配置 docker,使其可通过non-root用户使用;另一个是配置 docker 开机启动。

1)使用non-root用户操作 docker。

我们在 Linux 系统上操作,为了安全,需要以普通用户的身份登录系统并执行操作。所以,我们需要配置 docker,使它可以被 non-root 用户使用。具体配置方法如下:

  1. $ sudo groupadd docker # 1. 创建 `docker` 用户组
  2. $ sudo usermod -aG docker $USER # 2. 将当前用户添加到 `docker` 用户组下
  3. $ newgrp docker # 3. 重新加载组成员身份
  4. $ docker run hello-world # 4. 确认能够以普通用户使用 docker

如果在执行 sudo groupadd docker 时报 groupadd: group 'docker' already exists 错误,说明 docker 组已经存在了,可以忽略这个报错。

如果你在将用户添加到 docker 组之前,使用 sudo 运行过 docker 命令,你可能会看到以下错误:

  1. WARNING: Error loading config file: /home/user/.docker/config.json -
  2. stat /home/user/.docker/config.json: permission denied

这个错误,我们可以通过删除 ~/.docker/ 目录来解决,或者通过以下命令更改 ~/.docker/ 目录的所有者和权限:

  1. $ sudo chown "$USER":"$USER" /home/"$USER"/.docker -R
  2. $ sudo chmod g+rwx "$HOME/.docker" -R

2)配置 docker 开机启动。

配置命令如下:

  1. $ sudo systemctl enable docker.service # 设置 docker 开机启动
  2. $ sudo systemctl enable containerd.service # 设置 containerd 开机启动

安装 Kind 集群

Kind (Kubernetes in Docker)是一个工具。可以在本地快速创建、删除 Kubernetes 集群。Kind 是我用过的所有 Kubernetes 集群管理工具中最简单的一个。它的学习成本最低,对用户操作也最友好。

安装完 docker 之后,接下来我们就要部署一个 Kubernetes 集群。最简单的方式是通过 kind 来创建一个 kind 集群。此步骤又分为以下几步:

  1. 安装 kindkubectl 命令;

  2. 创建 kind 集群;

  3. 访问 kind 集群。

  4. 安装 kindkubectl 命令

安装命令如下:

  1. $ curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.17.0/kind-linux-amd64
  2. $ chmod +x ./kind
  3. $ sudo mv ./kind /usr/local/bin/kind
  4. $ kind version # 验证 kind
  5. kind v0.14.0 go1.18.2 linux/amd64

为了访问 Kind 集群,我们需要安装 kubectl 命令,安装命令如下:

  1. $ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
  2. $ chmod +x kubectl
  3. $ sudo mv kubectl /usr/bin/
  4. $ kubectl version --client --output=yaml # 验证 kubectl 是否安装成功
  5. clientVersion:
  6. buildDate: "2022-08-23T17:44:59Z"
  7. compiler: gc
  8. gitCommit: a866cbe2e5bbaa01cfd5e969aa3e033f3282a8a2
  9. gitTreeState: clean
  10. gitVersion: v1.25.0
  11. goVersion: go1.19
  12. major: "1"
  13. minor: "25"
  14. platform: linux/amd64
  15. kustomizeVersion: v4.5.7
  1. 创建 Kind 集群

安装好 kind 命令后,就可以通过 kind 命令来创建一个 Kubernetes 集群,创建命令如下:

  1. $ kind create cluster --name=miniblog-cluster
  2. Creating cluster "miniblog-cluster" ...
  3. Ensuring node image (kindest/node:v1.24.0) 🖼
  4. Preparing nodes 📦
  5. Writing configuration 📜
  6. Starting control-plane 🕹️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️
  7. Installing CNI 🔌
  8. Installing StorageClass 💾
  9. Set kubectl context to "kind-miniblog-cluster"
  10. You can now use your cluster with:
  11. kubectl cluster-info --context kind-miniblog-cluster
  12. Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
  13. $ kind get clusters # 查询 Kind 集群列表
  14. miniblog-cluster

上述命令会成功创建一个 Kubernetes 集群,并将 kubectl 命令的 context 设置为新创建的 Kind 集群。

相应的你可以通过 kind delete cluster --name=miniblog-cluster 来删除所创建的集群。

  1. 访问 Kind 集群

我们通过 kubectl cluster-info 来访问新建的 Kind 集群,以验证集群成功创建。访问命令如下:

  1. $ kubectl cluster-info
  2. Kubernetes control plane is running at https://127.0.0.1:37949
  3. CoreDNS is running at https://127.0.0.1:37949/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
  4. To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

部署 miniblog

上面,我们安装了 Docker,并使用 kind 部署了一个 Kubernetes 集群。接下来,我们就可以在 Kubernetes 集群中部署 miniblog 应用。部署分为以下 2 步:

  1. 修改配置;
  2. 部署 miniblog 组件;
  3. 测试 miniblog 服务是否部署成功。

修改配置

  1. 创建一个可以通过任意机器访问 miniblog 数据库的用户。

我们需要在容器中访问本地的数据库,所以需要创建一个可以通过任意机器访问的数据库用户,以便从容器中访问数据库,创建命令如下:

  1. $ mysql -uroot -p'miniblog1234'
  2. MariaDB [(none)]> grant all on miniblog.* TO 'docker' @ '%' identified by 'miniblog1234' ;
  3. Query OK, 0 rows affected (0.002 sec)
  4. MariaDB [(none)]> flush privileges;
  5. Query OK, 0 rows affected (0.001 sec)
  1. 修改 miniblog.yaml

修改 configs/miniblog.yaml 文件,更改其中的:tls.certtls.keydb.hostdb.usernamedb.password 配置。

tls.certtls.key 分别设置为:/etc/miniblog/cert/server.crt/etc/miniblog/cert/server.key

db.usernamedb.password 设置为第 1 步中设置的用户名和密码。db.host 设置为数据库所在的真实 IP 地址(例如:外网 IP 地址)。

修改后的 miniblog.yaml 文件内容为:

  1. # 通用配置
  2. runmode: debug # Gin 开发模式, 可选值有:debug, release, test
  3. addr: :8080 # HTTP 服务器监听地址
  4. jwt-secret: Rtg8BPKNEf2mB4mgvKONGPZZQSaJWNLijxR42qRgq0iBb5 # JWT 签发密钥
  5. # HTTPS 服务器相关配置
  6. tls:
  7. addr: :8443 # HTTPS 服务器监听地址
  8. cert: /etc/miniblog/cert/server.crt # 证书
  9. key: /etc/miniblog/cert/server.key # 证书 Key 文件
  10. # GRPC 相关配置
  11. grpc:
  12. addr: :9090 # GRPC 服务器监听地址
  13. # MySQL 数据库相关配置
  14. db:
  15. host: xx.xx.xx.xx # 注意替换这里的 IP 为实际的容器中可访问的 IP 地址
  16. username: docker # MySQL 用户名(建议授权最小权限集)
  17. password: miniblog1234 # MySQL 用户密码
  18. database: miniblog # miniblog 系统所用的数据库名
  19. max-idle-connections: 100 # MySQL 最大空闲连接数,默认 100
  20. max-open-connections: 100 # MySQL 最大打开的连接数,默认 100
  21. max-connection-life-time: 10s # 空闲连接最大存活时间,默认 10s
  22. log-level: 4 # GORM log level, 1: silent, 2:error, 3:warn, 4:info
  23. # 日志配置
  24. log:
  25. disable-caller: false # 是否开启 caller,如果开启会在日志中显示调用日志所在的文件和行号
  26. disable-stacktrace: false # 是否禁止在 panic 及以上级别打印堆栈信息
  27. level: debug # 指定日志级别,可选值:debug, info, warn, error, dpanic, panic, fatal
  28. format: console # 指定日志显示格式,可选值:console, json
  29. 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 文件。

  1. 制作 Docker image

执行以下命令构建 Docker image:

  1. $ make image

miniblog 的 Dockerfile 文件为:./build/docker/miniblog/Dockerfile

  1. 部署 ConfigMap。

先生成 CA 证书,命令如下:

  1. $ make ca

生成的 CA 证书文件保存在 _output/cert 目录下。

创建 miniblogminiblog-cert ConfigMap,创建命令如下:

  1. $ kubectl create configmap miniblog --from-file=configs/miniblog.yaml
  2. $ kubectl create configmap miniblog-cert --from-file=./_output/cert
  3. $ kubectl get cm | grep miniblog
  4. miniblog 1 81s
  5. miniblog-cert 8 76s
  1. Deployment 。

部署命令如下:

  1. $ kubectl create -f deployments/miniblog-deployment.yaml
  2. $ kubectl get pods
  3. NAME READY STATUS RESTARTS AGE
  4. miniblog-695c787f9f-s8sg7 0/1 Running 0 9s
  5. miniblog-695c787f9f-zmdwf 0/1 Running 0 9s

可以看到 miniblog Deployment 成功创建了 2 个 POD:miniblog-695c787f9f-s8sg7miniblog-695c787f9f-zmdwf

  1. 部署 Service。
  1. $ kubectl create -f deployments/miniblog-service.yaml
  2. $ kubectl get svc |grep miniblog
  3. 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 的部署实例:

  1. $ kubectl get pods
  2. NAME READY STATUS RESTARTS AGE
  3. miniblog-695c787f9f-s8sg7 1/1 Running 0 3m17s
  4. miniblog-695c787f9f-zmdwf 1/1 Running 0 3m17s

登录到其中一个实例上,请求 miniblog 的 /healthz 接口:

  1. $ kubectl exec -it miniblog-695c787f9f-s8sg7 -- bash
  2. [root@miniblog-695c787f9f-s8sg7 miniblog]# curl http://10.96.235.74:8080/healthz
  3. {"status":"ok"}

请求 /healthz 接口,成功返回 {"status":"ok"} 说明服务正常工作。

这里需要注意,我们需要通过 miniblog 服务 IP 10.96.235.74 访问 miniblog 的后端实例。通过 Kubernetes Service 访问 miniblog 服务,可以将请求动态负载到后端的部署实例上。

小结

本节课带你一步一步在 kind 集群中成功部署了 miniblog 服务。本节课旨在通过云原生世界中最核心的操作实战,带你进入云原生世界。根据需要自行学习云原生相关知识,以提高自己的研发能力。