使用 GitLab Auto DevOps 在 Kubernetes 上部署 Yii2 应用

首先,感谢 Guillaume Simon 的 Deploying Symfony test applications on Kubernetes with GitLab Auto DevOps, k3s and Let’s Encrypt – A 30m guide 一文给予我最大的帮助,使得本文能够完成。

其次,这里只涉及到部署。也就是只包含下列内容:

在未来可能会支持:

也希望有人在这方面为此项目创建拉取请求

最后,相信你看得懂上面的文字说明,并为此而来。我们继续~

在 Ubuntu 上部署 GitLab + MicroK8s

部署要求:

  • GitLab 12.6+
  • Kubernetes 1.12+

如果没有现存的部署环境,那么需要:

  • 一台至少 8GB 内存、2 核 CPU、100G 硬盘的 vps、实体主机或本地 VirtualBox 虚拟机
  • 可选的自有域名与 SSL 证书

两台 4GB 内存的实体主机也是可行,但不建议在同一宿主机配置两台虚拟机(除非宿主机 CPU 核心足够多)。

拥有公网 IP 且 80 与 443 端口可用,可以直接使用 le-http 自动配置 SSL。并且不需要配置自有域名,采用 nip.io 的 IP 域名即可。如果仅在内网使用,要使用 https 就需要一个或两个二级域名以及其对应的 SSL 证书。仅使用一个域名时,容器镜像库将使用指定端口与同一域名下的 GitLab 区分。

本示例默认将使用 VirtualBox 虚拟机和仅 http 无 SSL 的 nip.io 域名部署。

scoop 安装软件包

建议使用 scoop 安装 dockergithelmkubectlvirtualbox-np/virtualbox52-np(建议 5.2)软件包。

以下脚本示例均使用 Git Bash 作为 Shell。

其中需要 scoop install helm@2.16.1 然后 scoop hold helm 或者在升级 3.0 版本后使用 scoop reset helm@2.16.1

另外,默认的 helm 安装没有将 tiller 加入到 ~/scoop/shims 目录中,需要:

cp ~/scoop/shims/helm.exe ~/scoop/shims/tiller.exe
cp ~/scoop/shims/helm.ps1 ~/scoop/shims/tiller.ps1
cp ~/scoop/shims/helm.shim ~/scoop/shims/tiller.shim
sed -i 's/helm.exe/tiller.exe/g' ~/scoop/shims/tiller.ps1
sed -i 's/helm.exe/tiller.exe/g' ~/scoop/shims/tiller.shim

为了方便执行命令,可以设置 helm 与 kubectl 的 Bash 下 Tab 键自动完成:

mkdir -p ~/.helm && helm completion bash > ~/.helm/completion.bash.inc
mkdir -p ~/.kube && kubectl completion bash > ~/.kube/completion.bash.inc
echo "source '$HOME/.helm/completion.bash.inc'" >> ~/.profile
echo "source '$HOME/.kube/completion.bash.inc'" >> ~/.profile

Docker & docker-machine

另外,虚拟机使用的 Host-Only 网卡默认为 docker-machine 使用的 192.168.99.0/24 网段。如果不清楚如何配置,请使用 docker-machine create 创建默认的 docker 虚拟机即可。后续有些操作也会用到本地 docker 加速(解决)服务器 CI 构建缓慢(失败)的问题,详细内容请参阅本文末尾的 FAQ 内容。

具体的 docker-machine 创建脚本如下:

export CI_REGISTRY=registry.192-168-99-8.nip.io
export REGISTRY_MIRROR=https://dockerhub.azk8s.cn
docker-machine create default --engine-registry-mirror=$REGISTRY_MIRROR --engine-insecure-registry=http://$CI_REGISTRY

其中 CI_REGISTRY 仅在无 SSL 证书情况下指定,REGISTRY_MIRROR 为镜像配置,无网络因素时也可以不使用。

如果创建时自动下载 boot2docker.iso 文件缓慢或失败,请使用其他工具或方法下载对应文件到 ~/.docker/machine/cache/ 目录。也希望有组织可以提供 boot2docker.iso 镜像源,并提交工单以修改此处内容。

VirtualBox 安装 Ubuntu 18.04

虚拟机使用 Ubuntu 18.04 作为基础系统,请从上交大镜像下载安装镜像。

创建虚拟机类型为 Linux,版本为 Ubuntu (64bit),内存 8192 MB,硬盘 100.00 GB。创建完成后先不用启动,设置系统处理器的数量为 2;设置网络网卡 2 启用网络连接,连接方式为仅主机 (Host-Only) 网络,界面名称选择与 docker-machine 创建的 default 虚拟机相同的 Adapter。 (具体查看 default 虚拟机配置)

启动虚拟机,选择启动盘为已下载的 ubuntu-18.04.3-live-server-amd64.iso 文件。

不建议使用中文作为系统语言,默认 English。网络配置会显示两块网卡,默认都会 DHCP 配置;其中 10.0.2.15/24 是作为外网 NAT 访问用,不需要修改;另一块网卡则是内网使用,不建议使用 DHCP 需要修改为静态 IP,单机部署更需要设置两个静态 IP,当前先只配置一个 IP,选择该网卡 Edit IPv4IPv4 Method 选择 ManualSubnet 为 192.168.99.0/24Address 为 192.168.99.8,其他项目留空(不使用该网卡访问外网也就不需要设置网关与域名解析)。Proxy 默认为空。Mirror 建议修改为 http://mirrors.aliyun.com/ubuntu。硬盘分区使用默认的 Use An Entire Disk。创建用户并选择 Install OpenSSH Server,从 Github.com 拉取公钥。

注意,如果网速没问题,可以在 Snaps 安装页面直接选择安装 microk8s;否则先不选择,安装完系统再说。

系统安装成功重启后,可以选择 ssh 登录系统。虚拟机也可以使用无界面启动节省宿主机资源。

然后,修改虚拟机 IP:

sudo nano /etc/netplan/50-cloud-init.yaml

在:

            - 192.168.99.8/24

之后增加一行:

            - 192.168.99.9/24

执行:

sudo netplan apply

如果采用双机部署,请分别安装系统,无需配置多个 IP。

安装配置 MicroK8s

没有在系统安装时选择 microk8s 需要先手工安装:

sudo snap install microk8s --classic

其实系统自动安装 microk8s 的进程也是在重启后自动在后台运行安装。前台安装的方便之处在于发现下载速度过慢,可以 Ctrl+C 中止再重试,有一定几率尝试到 1+ Mbs 的下载速度。

相当多的文档会提到安装旧版本的 microk8s,因为新版不再内置 docker 而是使用 containerd。经过试用之后,感觉最新版本要比指定版本的旧版方便许多。当然除了自带的 microk8s.ctr 还不支持 image tag 之外。

使用 sudo microk8s.status 可以查看运行状态。如有问题,使用 sudo microk8s.inspect 检查。

如果网络存在无法拉取 gcr.io 容器镜像库的情况,需要先调整 containerd:

sudo nano /var/snap/microk8s/current/args/containerd-template.toml

找到 [plugins] 下 [plugins.cri] 的 sandbox_image,修改为:

    sandbox_image = "gcr.azk8s.cn/google_containers/pause:3.1"

然后继续往下 [plugins.cri.registry] 下 [plugins.cri.registry.mirrors] 删除:

        [plugins.cri.registry.mirrors."docker.io"]
          endpoint = ["https://registry-1.docker.io"]

增加:

        [plugins.cri.registry.mirrors."docker.io"]
          endpoint = ["https://dockerhub.azk8s.cn"]
        [plugins.cri.registry.mirrors."quay.io"]
          endpoint = ["https://quay.azk8s.cn"]
        [plugins.cri.registry.mirrors."gcr.io"]
          endpoint = ["https://gcr.azk8s.cn/google_containers/"]
        [plugins.cri.registry.mirrors."k8s.gcr.io"]
          endpoint = ["https://gcr.azk8s.cn/google_containers/"]
        [plugins.cri.registry.mirrors."registry.192-168-99-8.nip.io"]
          endpoint = ["http://registry.192-168-99-8.nip.io"]

Ctrl+X 保存后退出。其中最后部分仅针对无 SSL 证书的情况。

继续修改 kube-apiserver:

sudo nano /var/snap/microk8s/current/args/kube-apiserver

增加:

--runtime-config=apps/v1beta1=true,apps/v1beta2=true,extensions/v1beta1/daemonsets=true,extensions/v1beta1/deployments=true,extensions/v1beta1/replicasets=true,extensions/v1beta1/networkpolicies=true,extensions/v1beta1/podsecuritypolicies=true
--allow-privileged=true

支持 chart 脚本与 dind 提权。

重启 microk8s:

sudo microk8s.stop && sudo microk8s.start

无误后,开启 dns、 storage 与 metallb 组件:

sudo microk8s.enable dns storage metallb

其中 dns 用于内部寻址,storage 用于持久化存储,metallb 用于 Load Balancer 分配 Ingress 外部 IP。

启用过程中会提示输入 MetalLB 的 IP 范围,输入 192.168.99.9-192.168.99.9

使用 sudo microk8s.kubectl -n kube-system get pods 查看组件部署状态。

使用 sudo microk8s.config 查看 kubectl 配置,复制到本地 ~/.kube/config。需要替换默认的 10.0.2.15 IP 换成 192.168.99.9

在宿主机本地测试 kubectl -n kube-system get pods 命令。

安装配置 GitLab CE

增加软件源并指定域名安装:

export GITLAB_URL=http://gitlab.192-168-99-8.nip.io
# export GITLAB_MIRROR=https://packages.gitlab.com/gitlab
export GITLAB_MIRROR=https://mirrors.tuna.tsinghua.edu.cn
curl https://packages.gitlab.com/gpg.key 2> /dev/null | sudo apt-key add - &>/dev/null
echo "deb $GITLAB_MIRROR/gitlab-ce/ubuntu/ bionic main" | sudo tee /etc/apt/sources.list.d/gitlab-ce.list
sudo apt-get update
sudo EXTERNAL_URL=$GITLAB_URL apt-get install gitlab-ce

参见 https://about.gitlab.com/install/#ubuntu?version=ce 与 https://mirrors.tuna.tsinghua.edu.cn/help/gitlab-ce/ 说明。

修改 gitlab 配置:

sudo nano /etc/gitlab/gitlab.rb

找到:

# registry_external_url 'https://registry.example.com'

修改为:

registry_external_url 'http://registry.192-168-99-8.nip.io'

如果是单机部署,需要配置 nginx IP;找到:

# nginx['listen_addresses'] = ['*', '[::]']

修改为:

nginx['listen_addresses'] = ['192.168.99.8']

另外在:

# registry_nginx['listen_port'] = 5050

后面增加一行:

registry_nginx['listen_addresses'] = ['192.168.99.8']

生效 gitlab 配置:

sudo gitlab-ctl reconfigure

可以使用 sudo gitlab-ctl diff-config 查看配置修改项。

默认情况下,使用 EXTERNAL_URL 为 https 时,GitLab Omnibus 可以使用 Let’s Encrypt 自动配置 SSL 证书。但这需要外网 IP 且 80 与 443 端口可用(不能修改为其他端口)。

对于内网和虚拟机环境,采用自有域名和手工申请的 SSL 证书可以减少无 https 需要 insecure-registry 的额外设置。

安装完成 gitlab 后,需要先解压复制到 /etc/gitlab/ssl 目录:

sudo mkdir -p /etc/gitlab/ssl
sudo chmod 700 /etc/gitlab/ssl
sudo cp gitlab.example.com.key gitlab.example.com.crt /etc/gitlab/ssl/
sudo cp registry.example.com.key registry.example.com.crt /etc/gitlab/ssl/

参见 [https://docs.gitlab.com/omnibus/settings/nginx.html#manually-configuring-https]https://docs.gitlab.com/omnibus/settings/nginx.html#manually-configuring-https)

如果 registry 与 gitlab 使用同一域名则需要,修改:

registry_external_url 'https://gitlab.example.com:4567'
registry_nginx['ssl_certificate'] = "/etc/gitlab/ssl/gitlab.example.com.crt"
registry_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/gitlab.example.com.key"

参见 https://docs.gitlab.com/ee/administration/packages/container_registry.html#configure-container-registry-under-an-existing-gitlab-domain

修改 GitLab Helm 安装命令

可以修改 gitlab helm 命令代码来加速 GitLab Managed Apps 的 helm 安装。

sudo nano /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/kubernetes/helm/init_command.rb

修改:

        def tls_flags
          [
            '--tiller-tls',
            '--tiller-tls-verify',
            '--tls-ca-cert', "#{files_dir}/ca.pem",
            '--tiller-tls-cert', "#{files_dir}/cert.pem",
            '--tiller-tls-key', "#{files_dir}/key.pem"
          ]
        end

为:

        def tls_flags
          [
            '--stable-repo-url', 'https://mirror.azure.cn/kubernetes/charts/',
            '--tiller-tls',
            '--tiller-tls-verify',
            '--tls-ca-cert', "#{files_dir}/ca.pem",
            '--tiller-tls-cert', "#{files_dir}/cert.pem",
            '--tiller-tls-key', "#{files_dir}/key.pem"
          ]
        end
sudo nano /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/kubernetes/helm/client_command.rb

修改:

        def init_command
          if local_tiller_enabled?
            <<~HEREDOC.chomp
            export HELM_HOST="localhost:44134"
            tiller -listen ${HELM_HOST} -alsologtostderr &
            helm init --client-only
            HEREDOC
          else
            # Here we are always upgrading to the latest version of Tiller when
            # installing an app. We ensure the helm version stored in the
            # database is correct by also updating this after transition to
            # :installed,:updated in Clusters::Concerns::ApplicationStatus
            'helm init --upgrade'
          end
        end

为:

        def init_command
          if local_tiller_enabled?
            <<~HEREDOC.chomp
            export HELM_HOST="localhost:44134"
            tiller -listen ${HELM_HOST} -alsologtostderr &
            helm init --client-only --stable-repo-url https://mirror.azure.cn/kubernetes/charts/
            HEREDOC
          else
            # Here we are always upgrading to the latest version of Tiller when
            # installing an app. We ensure the helm version stored in the
            # database is correct by also updating this after transition to
            # :installed,:updated in Clusters::Concerns::ApplicationStatus
            'helm init --upgrade --stable-repo-url https://mirror.azure.cn/kubernetes/charts/'
          end
        end

修改完成后,需要 sudo gitlab-ctl restart 重启 GitLab 服务。

在 GitLab 上配置 Kubernetes

访问 http://gitlab.192-168-99-8.nip.io 设置 root 密码后登录。

点击 Configure GitLab 左侧 Settings 下的 Network/admin/application_settings/network)。展开(Expand)Outbound requests,在白名单(Whitelist)填入 192.168.99.9

继续左侧 Kubernetes 中 Add Kubernetes cluster 标签栏 Add existing cluster

首先填入 Kubernetes cluster name 为 MicroK8sAPI URL 为 https://192.168.99.9:16443

参考 https://docs.gitlab.com/ee/user/project/clusters/add_remove_clusters.html#existing-gke-cluster

先使用 kubectl get secrets 选取一个 default-token-xxx,然后执行:

export SECRET_NAME=default-token-xxx
kubectl get secret $SECRET_NAME -o jsonpath="{['data']['ca\.crt']}" | base64 --decode

复制输出的证书内容到 CA Certificate

然后建立一个 gitlab-admin-service-account.yaml 文件,内容如下:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: gitlab-admin
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: gitlab-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: gitlab-admin
  namespace: kube-system

然后执行:

kubectl apply -f gitlab-admin-service-account.yaml

成功后,使用:

kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep gitlab-admin | awk '{print $1}')

获取 Data token 部分填入 Service Token

添加成功后,先设置 Base domain 为 192-168-99-9.nip.io

然后,依次安装 Helm TillerIngressGitLab Runner,可选安装 Prometheus,对于公网 IP 建议安装 Cert-Manager

安装时可以使用 kubectl -n gitlab-managed-apps logs install-runner -f 来监控安装日志。

如果 kubectl -n gitlab-managed-apps get pods 发现有 ImagePullBackOff 使用 kubectl -n gitlab-managed-apps describe pod ingress-nginx-ingress-default-backend-xxx-yyy 查看。

可以在虚拟机上使用类似 docker image tag 的操作:

export GCR_MIRROR=gcr.azk8s.cn/google_containers
export IMAGE_NAME=defaultbackend-amd64
export IMAGE_TAG=1.5
sudo microk8s.ctr image pull $GCR_MIRROR/$IMAGE_NAME:$IMAGE_TAG
sudo microk8s.ctr image export temp.tar $GCR_MIRROR/$IMAGE_NAME:$IMAGE_TAG
sudo microk8s.ctr image import --base-name k8s.gcr.io/$IMAGE_NAME temp.tar
sudo rm temp.tar

安装完毕后执行:

kubectl -n gitlab-managed-apps get svc ingress-nginx-ingress-controller

成功启用 MicroK8s MetalLB 组件后会自动分配好 EXTERNAL-IP 为 192.168.99.9

如果看到 EXTERNAL-IP 为 <pending>

参考 https://stackoverflow.com/a/54168660

可以指定 IP:

export EXTERNAL_IP=192.168.99.9
kubectl -n gitlab-managed-apps patch svc ingress-nginx-ingress-controller -p "{\"spec\": {\"type\": \"LoadBalancer\", \"externalIPs\":[\"$EXTERNAL_IP\"]}}"

最后修改 externalTrafficPolicy 配置,让 Ingress 可以拿到访问来源 IP:

kubectl -n gitlab-managed-apps edit svc ingress-nginx-ingress-controller

找到:

spec:
  externalTrafficPolicy: Cluster

修改为:

spec:
  externalTrafficPolicy: Local

注意:Pod 里面需要通过 HTTP 头 X-Forwarded-For 来获取 IP。

部署 Yii2 应用

从 GitHub 导入代码

使用 Import Project 选择 Repo by URL 填入 https://github.com/larryli/yii2-auto-devops.git 创建项目。

功能

首页、登录、退出、联系我们与关于

参见 yii2-app-basic

Post 表 CURD

使用 gii 生成的标准 yii2 curd 功能。

下载

使用后台队列下载然后通过 nchan 通知前台页面完成。

上传

使用 yii\web\UploadedFile 上传图像文件并显示。

组件

数据库

采用 MySQL 数据库。

缓存

采用 Redis 缓存,并为设置 replicas 去使用主从集群。

可选使用文件缓存,也就是缓存只存在与 pod 内部。在 scale 后无法在多个 pod 之间共享缓存,也就是无法利用缓存在不同请求之间交换数据。

注意:仅仅作为临时缓存(如页面缓存),除了性能问题是不存在其他问题的。

也可选使用数据库缓存,但需要注意启用数据库结构缓存创建缓存表存在冲突。

会话

采用 Redis 会话。

可选使用 PHP 系统会话,与文件缓存一样只支持单机。

也可选使用数据库会话。

队列

采用 Redis 队列。

可选使用文件队列,与文件缓存一样只支持单机。

也可选使用数据库队列。

前端资源

自动配置 asset-bundles.php

Redis

配置 Redis 服务。

Nchan

使用 yii\httpclient\Client 调用 nginx nchan pub 接口。在 web 配置下因为处于同一 pod 下无需使用 NCHAN_HOST

目录

runtime

应用运行时临时目录,其中 runtime/logs/app.log 为 Yii2 日志文件。在 pod 部署中挂载此目录为空目录,然后另外配置容器日志输出 app.log 内容。

vendor

此目录是 composer 自动下载的第三方包,会在构建阶段生成并包含在映像中。

web

前端内容。应用容器中 app(php-fpm)与 nginx 共享目录,仅在主 pod 中挂载此目录为空目录,并在 pod 生命周期开始时从 app 复制相关文件到 nginx。

web/assets

Yii2 前端资源目录。应用容器中 app(php-fpm)与 nginx 共享目录,仅在主 pod 中挂载此目录为空目录。

注意:在构建中使用 BUILD_ASSET = true 打包后,此目录为空,可选配置。当 BUILD_ASSET = false 时,此目录必须配置。

web/uploads

上传文件目录。应用容器中 app(php-fpm)与 nginx 共享目录,仅在主 pod 中挂载此目录为持久化目录。

CI / CD 设置

staging 与 production 部署方式

Auto DevOps 的 Deployment strategy 的三项设置:

  • Continuous deployment to production
  • Continuous deployment to production using timed incremental rollout
  • Automatic deployment to staging, manual deployment to production

会与 Variables 中的:

  • INCREMENTAL_ROLLOUT_MODE
  • STAGING_ENABLED

相互作用。

首先,Deployment strategy 的选择会决定 INCREMENTAL_ROLLOUT_MODE 和 STAGING_ENABLED 默认值,如下表所示:

Deployment strategyINCREMENTAL_ROLLOUT_MODESTAGING_ENABLED
Continuous deployment to productionfalse
Continuous deployment to production using timed incremental rollouttimedfalse
Automatic deployment to staging, manual deployment to productionmanualtrue

参见 https://docs.gitlab.com/ce/topics/autodevops/#deployment-strategy

所以,部署方式会有下列可能:

Deployment strategyINCREMENTAL_ROLLOUT_MODESTAGING_ENABLEDstagingproduction
Continuous deployment to production自动部署
Continuous deployment to productiontrue自动部署手动部署
Continuous deployment to productionmanual手动增量部署
Continuous deployment to productionmanualtrue自动部署手动增量部署
Continuous deployment to production using timed incremental rollout延后 5 分钟自动增量部署
Continuous deployment to production using timed incremental rollouttrue自动部署延后 5 分钟自动增量部署
Automatic deployment to staging, manual deployment to productionfalse手动增量部署
Automatic deployment to staging, manual deployment to production自动部署手动增量部署

实际六种部署方式,请按照需要预先选择好部署方式。

参见 https://docs.gitlab.com/ce/topics/autodevops/#incremental-rollout-to-production-premium

对于增量部署,可选的 ROLLOUT_STATUS_DISABLED = true 可以在部署日志中不显示状态信息。

review 部署

仅一个 REVIEW_DISABLED = false 关闭默认的自动在分支上审查应用功能。

附加域名

使用单独指定 Scope 的 ADDITIONAL_HOSTS 或具体 <env>_ADDITIONAL_HOSTS 如 PRODUCTION_ADDITIONAL_HOSTS 设置部署应用的附加域名。

域名可以为多个,以英文逗号分隔。如:domain.com, www.domain.com

<env>_ADDITIONAL_HOSTS 的优先级要比 ADDITIONAL_HOSTS 高。

https 访问 与 SSL 证书

当前默认启用了 TLS(即 https 访问)和启用 Cert-Manager 管理证书(自动从 Let’s Encrypt 申请)。禁用了 nginx.ingress.kubernetes.io/ssl-redirect(即访问 http 自动跳转 https)。

可以使用 TLS_SSL_REDIRECT = true 开启 http 自动跳转 https。

使用 TLS_ENABLED = false 关闭 https。

使用 TLS_ACME = false 禁用 Cert-Manager 自动从 Let’s Encrypt 申请证书。

使用单独指定 Scope 的 TLS_SECRET_NAME 或具体 <env>_TLS_SECRET_NAME 如 PRODUCTION_TLS_SECRET_NAME 设置部署应用的 SSL 证书 secret name。

<env>_TLS_SECRET_NAME 的优先级要比 TLS_SECRET_NAME 高。

可以使用下面的命令创建 secret tls 存放对应的证书与私钥:

export KUBE_NAMESPACE=yii2-auto-devops-1-production
export TLS_SECRET_NAME=production-tls
export CERT_FILE=$HOME/ssl-certs/example.com.crt
export KEY_FILE=$HOME/ssl-certs/example.com.key
kubectl -n $KUBE_NAMESPACE create secret tls $TLS_SECRET_NAME --cert=$CERT_FILE --key=$KEY_FILE

也可以指定 Type 为 File 的 TLS_CERT_FILE<env>_TLS_CERT_FILE)与 TLS_KEY_FILE<env>_TLS_KEY_FILE)分别存放证书与私钥。

注意:Type 为 File 不能勾选 Masked,也就是当使用 CI_DEBUG_TRACE = true 调试 CI 时会在日志中显示完整的证书与私钥内容。存在安全隐患,请一定要在调试完成后删除 CI 日志。

当指定 TLS_SECRET_NAME 时会强制 TLS_ENABLED = true 和 TLS_ACME = false

MySQL 数据库

可以使用 MYSQL_ENABLED = false 关闭默认的自动部署 MySQL 服务,从而使用外部 MySQL(可以手工在同一 Kubernets 上安装,也可以使用现有服务)。

当 MYSQL_ENABLED = false 时,建议按实际情况完整指定 MYSQL_HOSTMYSQL_DBMYSQL_USER 与 MYSQL_PASSWORD

当 MYSQL_ENABLED = true 时,一定不要设置 MYSQL_HOST,否则无法连接自动安装的 MySQL。MYSQL_DBMYSQL_USER 与 MYSQL_PASSWORD 可以按需指定。另外可以使用 MYSQL_VERSION 指定 MySQL 版本。

数据库初始化与迁移

  • DB_INITIALIZE = /app/wait-for -t 999 -- echo Initialized.
  • DB_MIGRATE = /app/yii migrate/up --interactive=0

其中 DB_INITIALIZE 直接使用 wait-for 脚本等待 k8s 创建 MySQL 服务成功。其中 HOST 与 PORT 是在环境变量中定义;无法在 DB_INITIALIZE 中直接使用 /app/wait-for $MYSQL_HOST:3306,因为 k8s 自动创建的 MYSQL_HOST 是在 auto-deploy 中才定义的(.gitlab-ci.yml 中引用 $MYSQL_HOST 会为空值)。

使用默认值即可,一般无需修改。

数据库结构缓存

使用 K8S_SECRET_ENABLE_SCHEMA_CACHE = true 开始缓存,可以配置缓存时间 K8S_SECRET_SCHEMA_CACHE_DURATION(单位:秒,默认 60 秒)和缓存实体 K8S_SECRET_SCHEMA_CACHE(默认 cache)。

当 Yii2 缓存也使用数据库缓存时,请不要一开始就开启此项。一定要部署成功后,后续部署时开启表结构缓存。否则会在建表前出现无法读到缓存(因为缓存表未建)的问题。

Redis

可以使用 REDIS_ENABLED = false 关闭默认的自动部署 Redis 单例服务,从而使用外部 Redis(可以手工在同一 Kubernets 上安装,也可以使用现有服务)。

当 REDIS_ENABLED = false 时,建议按实际情况完整指定 REDIS_HOSTREDIS_DB 与 REDIS_PASSWORD

当 REDIS_ENABLED = true 时,一定不要设置 REDIS_HOST,否则无法连接自动安装的 Redis。REDIS_DB 与 REDIS_PASSWORD 可以按需指定。另外可以使用 REDIS_VERSION 指定 Redis 版本。

当前 Yii2 缓存使用 Redis,并未设置 replicas。对于外部 Redis 建议使用 sentinel 哨兵集群。

后台队列任务

对于 Yii2 Queue 来说,默认为:

  • QUEUE_CMD = /app/yii queue/listen --verbose

设置为空时不使用后台队列任务。

Nchan

因为后台队列任务与前台 Web 应用在不同 Pod 中执行,所以提供有一个自动指定的 NCHAN_HOST 供后台队列使用。对于前台 Web 应用来说可以直接使用 localhost

定时任务

样例脚本和时间配置为:

  • CRON_CMD = /app/yii hello
  • CORN_SCHEDULE = */1 * * * *

任意一项为空即不使用定时任务。

K8S_SECRET_COOKIE_VALIDATION_KEY 是唯一一个必须配置的变量,对于 production 也建议使用 Scope 单独指定,并勾选 Masked

SMTP 邮件

配置 K8S_SECRET_SMTP_ENABLED = true 启用 SMTP 邮件发送功能,并配置下列参数:

  • K8S_SECRET_SMTP_HOST SMTP 主机
  • K8S_SECRET_SMTP_PASSWORD SMTP 密码
  • K8S_SECRET_SMTP_PORT SMTP 端口
  • K8S_SECRET_SMTP_TLS 是否使用 tls

Yii2 环境与调试以及开发构建

默认 YII_DEBUG = false YII_ENV = prod

可以使用 K8S_SECRET_YII_DEBUG 和 K8S_SECRET_YII_ENV 分别配置为 true 与 dev 开启调试。

并设置 K8S_SECRET_DEBUG_IP = * 允许所有 IP 可访问调试面板。

默认 BUILD_DEV = false 会在 Dockerfile 构建时 composer 使用 --no-dev 参数不在映像中包含调试(开发测试)功能。如果需要线上调试,请配置 BUILD_DEV = true 构建参数包含调试(开发测试)功能。否则会部署失败(无法加载 dev 相关包)。

注意:.gitlab-ci.yml 构建阶段(build stage)是没有定义环境,也就是无法配置范围(Scope)。只能配置所有环境(All environments)或者手工执行(建议)时指定 BUILD_DEV = trueK8S_SECRET_YII_DEBUG = trueK8S_SECRET_YII_ENV = dev 和 K8S_SECRET_DEBUG_IP = *(后三个支持环境范围配置)。

构建前端 assets 资源

默认 BUILD_ASSET = true,会在 Dockerfile 构建时对应用使用使用到的前端资源直接打包成独立的 js/css 文件。

具体的资源使用定义在 config/assets.php 中。如果发现前端仍有载入前端 web/assets 目录下的临时文件,请将对应的 Asset 类加入 config/assets.php 的 bundles 数组中。如果第三方包使用 CDN 也可以对应配置外部资源。

使用 BUILD_ASSET = false 可以关闭资源打包。

注意:.gitlab-ci.yml 构建阶段(build stage)是没有定义环境,也就是无法配置范围(Scope)。只能配置所有环境(All environments)或者手工执行(建议)时指定 BUILD_ASSET = false

其他变量

K8S_SECRET_ADMIN_EMAILK8S_SECRET_SENDER_EMAILK8S_SECRET_SENDER_NAME 均为演示目的。

注意:未配置 K8S_SECRET_SMTP_ENABLED = true 和其他 SMTP 变量时,无法发送邮件。

构建与部署技术架构

整个构建与部署是在 GitLab Auto DevOps 基础上完全自定义。

请先仔细阅读 https://docs.gitlab.com/ce/topics/autodevops/#customizing 的相关说明。

自定义 .gitlab-ci.yml

目前只使用 Build 和 Deploy 两个组件,所以只包含了 Auto-DevOps.gitlab-ci.ymlJobs/Build.gitlab-ci.yml 与 Jobs/Deploy.gitlab-ci.yml 三块内容。

首先,variables 增加了三部分内容:下载镜像源、MySQL 与数据库命令。

其次,stages 中删除了 test 等阶段。

然后是 Build 部分,针对 docker:stable-dind 服务修改了 entrypoint 入口。在 Build 的 variables 定义了下载镜像源(无网络问题可以去掉)与内部镜像源(非 http 的 CI_REGISTRY 需要,使用 https 可以去掉),以及并发下载数。再指明构建脚本为本地 ./build.sh 脚本。

最后的 Deploy 部分,除了 stop_review 外也一样将部署脚本切换为本地 ./auto-deploy 脚本。再删除了 canary 相关内容(GitLab EE Premium 功能)。stop_review 因为设置了 GIT_STRATEGY: none 不会检出项目文件,所以必须使用原始部署脚本。

自定义 Dockerfile

采用 Docker multi-stage 多阶段构建。避免在 app 映像中包含 composer 下载缓存与 php 开发包内容。

需要注意的是,composer 执行与 app 的 php 执行并不在同一个映像中。也就是 app 映像中并没有 composer 命令。

在 composer 构建阶段,使用了两个参数 BUILD_DEV 与 BUILD_ASSET

BUILD_DEV 切换直接使用了 --dev 和 --no-dev 两个 composer install 参数,以方便在 BUILD_DEV = true 时包含 test 测试与 debug 调试。

BUILD_ASSET = true 处理前端资源打包。需要注意两点:

  • bootstrap 除了 css/js 外还有字体文件,需要先复制到 web 前端目录中,再打包
  • 打包之后清理了相关资源目录

另外提供的 Dockerfile-nginx 时单独构建 nginx + nchan。该映像并未配置 nginx 自动载入 nchan 扩展。

自定义 build.sh

去掉了 buildpacks 相关的内容。并针对 multi-stage 构建在每一个构建阶段都 pull & push 阶段映像,以便构建缓存可以正常工作。还增加了构建参数。

另外也分段构建了 nginx + nchan。

自定义 chart

在 requirements.yaml 中使用 mysql 替换掉了 postgresql。默认使用最新的 1.x 版本,如需要兼容旧版本 kubernetes 请修改为 0.x 版本。修改后需更新 requirements.lock 文件,详见文末的 FAQ。

然后再增加了 redis。

在 values.yaml 同样使用 mysql 替换掉了 postgresql(没有实现对应 managed 逻辑)。增加了 nginx.ingress.kubernetes.io/ssl-redirect 控制是否自动从 http 跳转到 https。

扩充了 application 内容,配置后台队列任务的并发值 parallelismCount

增加了 redis 与 persistence

删除了不需要的 workers

注意:增加的 redis 关闭了主从集群,只使用了单主部署。

增加 Nginx 需要的 templates/nginx-configMap.yaml 配置文件。其中针对 nchan 配置了 redis 相关配置,这里的 redis database 是直接指定的 0 值。

增加上传文件需要的 templates/pvc.yaml 持久化存储配置文件。

修改 templates/db-initialize-job.yamltemplates/db-migrate-hook.yaml 和 templates/deployment.yaml,使用 MYSQL_HOST 等替换掉 DATABASE_URL 环境变量设置,增加 REDIS_HOST 等变量设置。还增加了 runtime 运行目录的配置。

增加定时任务 templates/cronJob.yaml 配置文件。

增加后台队列任务 templates/queue-worker-job.yaml 配置文件。默认并发作业配置为 2

在 templates/deployment.yaml 增加了五个 volumes 挂载入口,其中 assets 用于 nginx 与 php-fpm 共享 yii2 生成的前端资源文件(BUILD_ASSET = true 时可不需要此配置),nginx-config-volume 为 nginx 配置,runtime 为应用运行临时目录(应用日志 logs/app.log 在此目录下),shared-files 将 php 入口文件与 css、js 等资源文件复制到 nginx,uploads 挂载的是持久化存储用于存放应用上传文件。并在后面的三个容器中分别挂载。同时在 app 主容器最后增加 postStart 生命周期,执行日志初始化与前端复制操作(使用 tar 而不是 cp 是因为 cp 无法排除 assets 与 uploads 目录,而且映像中没有提供 rsync)。

在 containers 后增加了 log 与 nginx 容器。应用日志直接在 log 容器下输出。Pod 存活检查移到 nginx 容器下,另外增加了 nchan 配置的 9090 端口。

同样在 service.yaml 也增加了 nchan 配置的 9090 端口。

删除了 worker-deployment.yaml 文件。

自定义 auto-devops

在开始部分,将 DATABASE_URL 逻辑修改为 MYSQL_HOST。并类似处理 REDIS_HOST 逻辑,增加了 NCHAN_HOST 处理逻辑。

小幅调整了 download_chart

主要修改在 deploy 的 helm upgrade --install--force 参数是为了解决后台队列任务更新部署时无法自动删除旧有任务的问题。

然后额外处理了 SSL 证书相关的逻辑。

优化指南

本项目的代码是让一个独立项目展示所有技术细节。对于实际的项目,相关优化必不可少;否则缓慢的重复构建,多个项目之间的重复代码也让维护头疼。

Docker 预先构建

对于 composerphp 与 nginx 可以创建三个内部项目单独构建,应用项目直接使用即可。

其中 composer 的基础内容与项目内容要分拆,即只拆出 COPY composer.* /app 之前的内容。

Auto DevOps

auto-build-image

建议按照官方项目去掉 ruby(Dockerfile.erb)与 herokuish(BUILDPACK)相关支持重新构建。

另外,构建脚本可以增加一个可选的 BUILD_TARGET 阶段构建选项来确定是否缓存中间阶段映像。

auto-deploy-image

建议在官方项目的基础上修改,覆盖掉部署脚本。

首先,可以去掉 AUTO_DEVOPS_CHART 下载逻辑,只支持项目本地 chart。

其中,不同项目的 chart 不同,会导致 helm upgrade --install 的参数变化。除了有效利用 HELM_UPGRADE_EXTRA_ARGS 来传参外,建议另外定义一个 DEPLOY_SCRIPT 脚本来处理相关服务的逻辑,替换掉默认的操作。

auto-deploy-chart

如果许多项目存在共同的部署模板,可以考虑修改。但 chart 不能使用 git 项目,需要另外部署服务。

Auto-DevOps.gitlab-ci.yaml

自定义 auto-build-image 与 auto-deploy-image 后,就可以修改官方脚本使用 include file 引用。

而 Auto-DevOps.gitlab-ci.yaml 具体内容可以直接:

include:
  - template: Jobs/Build.gitlab-ci.yml
  - template: Jobs/Deploy.gitlab-ci.yml

# https://gitlab.com/gitlab-org/gitlab-foss/blob/master/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml
build:
  image: "registry.192-168-99-8.nip.io/devops/auto-build-image"
  variables:
    DOCKER_CONCURRENT: 6
    DOCKER_DAEMON_OPTIONS: "--registry-mirror=${REGISTRY_MIRROR} --insecure-registry=${CI_REGISTRY} --max-concurrent-downloads=${DOCKER_CONCURRENT}"
  services:
    # https://gitlab.com/gitlab-org/gitlab-runner/issues/3808#note_244570527
    - name: docker:stable-dind
      entrypoint: [ "sh", "-c", "dockerd-entrypoint.sh ${DOCKER_DAEMON_OPTIONS}" ]

# https://gitlab.com/gitlab-org/gitlab-foss/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
.auto-deploy:
  image: "registry.192-168-99-8.nip.io/devops/auto-deploy-image"

FAQ

使用本地 Docker 缓存加速(解决) CI 构建缓慢(失败)

如果服务器 CI 构建出现问题,可以选择在本地使用 Docker 构建后推到容器镜像库。当然,不同分支切换时因为缓存不存在,也可以拉取其他分支的镜像缓存回来,通过 tag 改名再推到容器镜像库作为缓存加速服务器 CI 执行。

Tag 改名:

export CI_REGISTRY=registry.192-168-99-8.nip.io
export PROJECT=root/yii2-auto-devops
export SOURCE_BRANCH=master
export BRANCH=foobar
docker login $CI_REGISTRY
docker pull $CI_REGISTRY/$PROJECT/$SOURCE_BRANCH:composer
docker tag $CI_REGISTRY/$PROJECT/$SOURCE_BRANCH:composer $CI_REGISTRY/root/yii2-auto-devops/$BRANCH:composer
docker push $CI_REGISTRY/$PROJECT/$BRANCH:composer
docker pull $CI_REGISTRY/$PROJECT/$SOURCE_BRANCH:builder
docker tag $CI_REGISTRY/$PROJECT/$SOURCE_BRANCH:builder $CI_REGISTRY/root/yii2-auto-devops/$BRANCH:builder
docker push $CI_REGISTRY/$PROJECT/$BRANCH:builder

本地构建:

export CI_REGISTRY=registry.192-168-99-8.nip.io
export PROJECT=root/yii2-auto-devops
export BRANCH=foobar
docker login $CI_REGISTRY
docker build --target composer --tag $CI_REGISTRY/$PROJECT/$BRANCH:composer .
docker push $CI_REGISTRY/$PROJECT/$BRANCH:composer
docker build --target composer --tag $CI_REGISTRY/$PROJECT/$BRANCH:builder .
docker push $CI_REGISTRY/$PROJECT/$BRANCH:builder
docker build --tag $CI_REGISTRY/$PROJECT/$BRANCH:latest .
docker push $CI_REGISTRY/$PROJECT/$BRANCH:latest

注意:本地构建会因为 Windows 与 Linux 文件系统不同造成差异,从而使得缓存失效。

更新 chart requirements.lock

export CHART_MIRROR=https://mirror.azure.cn/kubernetes/charts/
helm init --client-only --stable-repo-url $CHART_MIRROR
helm dependency update chart/

测试 chart 模板

helm template chart/

其他 --set 参数可以参考 auto-deploy 脚本中对应的内容。

部署出错提示 Error: error installing: namespaces “staging” not found

不小心直接 kubectl delete namespace 会出现此问题。

请使用 GitLab 12.6.0 以上版本,在 Kubernetes 的 Advanced settings 中点击 Clear cluster cache

参见 https://docs.gitlab.com/ee/user/project/clusters/index.html#clearing-the-cluster-cache

部署出错提示 Error: UPGRADE FAILED: “staging” has no deployed releases

一般会出现在上一次部署失败或被取消后。

在本地使用 helm 清除(请先配置 kubectl config):

export CHART_MIRROR=https://mirror.azure.cn/kubernetes/charts/
export KUBE_NAMESPACE=yii2-auto-devops-1-staging
export CHART_NAME=staging
export TILLER_NAMESPACE=$KUBE_NAMESPACE
tiller -listen localhost:44134 &
# export TILLER_PID=
export HELM_HOST="localhost:44134"
helm init --client-only --stable-repo-url $CHART_MIRROR
helm ls
helm delete $CHART_NAME --purge --tiller-namespace $KUBE_NAMESPACE
# kill -9 $TILLER_PID

参见 https://gitlab.com/gitlab-org/gitlab-foss/issues/54760

感谢

对上文中提到的所有 mirrors 维护者表示衷心的感谢!

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据