• Post author:
  • Post category:Kubernetes
  • Post comments:0评论

1、背景环境

现将个人博客迁往 K8S 中,个人博客使用 WordPress,典型的 LNMP 架构。
环境:

• K8S 版本为 1.22,单节点。
• MySQL 为一主一从集群,采用 StatefulSet 部署,位于 mysql 命名空间。
• Nginx 和 PHP,采用 Deployment 部署,位于 blog 命名空间。 
• Nginx 和 PHP 的 Service 使用 ClusterIP,使用 Ingress 进行转发。
• 数据持久化,采用 Local 类型 PV,已提前准备好 Local Storage Class。

2、准备迁移数据

先挂个维护界面吧:

# 使用 rewrite 将用户请求全部跳转到维护页面
server {
     # ......省略
     # 所有页面都转跳到维护页
     rewrite ^(.*)$ /maintain.html break;
}

# 维护页面
[root@cp ~]# cat /wordpress/maintain.html 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<h1>网站正在维护中</h1>

准备好迁移数据:

[root@cp ~]# mysqldump -u root -p -B wordpress_blog  > wordpress-blog$(date +%F).sql
[root@cp ~]# zip -qr wordpress-blog$(date +%F).zip /web-project/wordpress-blog
[root@cp ~]# ll *blog*
-rw-r--r-- 1 root root  42914232 Jan 13 21:08 wordpress-blog2022-01-13.sql
-rw-r--r-- 1 root root 317531310 Jan 13 18:40 wordpress-blog2022-01-13.zip

3、部署 MySQL 主从集群

部署过程:请参阅 部署 MySQL 主从集群,注意本例 MySQL 集群属于 mysql 命名空间。
导入数据:

# 导入sql数据
[root@cp ~]# kubectl exec mysql-0 -it -n mysql -- mysql < wordpress-blog2022-01-13.sql

# 验证
[root@cp ~]# kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never -n mysql -- mysql -h mysql-read -e "show databases;"
If you don't see a command prompt, try pressing enter.
+------------------------+
| Database               |
+------------------------+
| information_schema     |
| mysql                  |
| performance_schema     |
| sys                    |
| wordpress_blog         |
| xtrabackup_backupfiles |
+------------------------+
pod "mysql-client" deleted

创建用户:

# 创建wordpress远程用户
[root@cp ~]# kubectl exec mysql-0 -it -n mysql -- mysql -e "grant all on wordpress_blog.* to wordpress@'%' identified by '***********';"

# 验证
[root@cp ~]# kubectl exec mysql-0 -it -n mysql -- mysql -e "select user,host,authentication_string from mysql.user where user='wordpress';"
Defaulted container "mysql" out of: mysql, xtrabackup, init-mysql (init), clone-mysql (init)
+-----------+------+--------------------------+
| user      | host | authentication_string    |
+-----------+------+--------------------------+
| wordpress | %    | ************             |
+-----------+------+--------------------------+

  在本例中,MySQL 集群属于 mysql 命名空间,而 Nginx+PHP 属于 blog 命名空间,这就涉及到跨命名空间访问服务。
  那么如何访问,这分情况:
  同一集群相同命名空间中的任何 Pod 中,通过解析 <Pod 名称>.<Service 名称>(例如:mysql-0.mysql)来访问数据库。
  同一集群不同命名空间中的任何 Pod 中,通过解析 <Pod 名称>.<Service 名称>.<Namespace 名称>(例如:mysql-0.mysql.mysql)来访问数据库。

4、PHP 镜像构建

构建的 PHP 镜像,包含了 WordPress 所需要的 PHP 扩展。

[root@cp php-fpm]# cat dockerfile 
FROM php:7.4.27-fpm
RUN apt-get update && apt-get install -y libfreetype6-dev libjpeg62-turbo-dev libmcrypt-dev libpng-dev zlib1g-dev libzip-dev \
    && docker-php-ext-install gd pdo pdo_mysql mysqli exif zip \
    && apt-get install -y --no-install-recommends libmagickwand-dev \
    && pecl install imagick-3.4.3 \
    && docker-php-ext-enable imagick \
    && apt-get clean

[root@cp php-fpm]# docker build -t cp/php:fpm-wordpress .

5、创建 configmap

Nginx:

[root@cp blog]# cat nginx-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
  namespace: blog
data:
  nginx.conf: |
    user  www-data;
    worker_processes auto;
    error_log  /var/log/nginx/error.log warn;
    pid        /var/run/nginx.pid;

    events {
        worker_connections  1024;
    }

    http {
        include       /etc/nginx/mime.types;
        default_type  application/octet-stream;

        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';

        access_log  /var/log/nginx/access.log  main;

        set_real_ip_from 10.244.0.0/16;
        real_ip_header X-Forwarded-For;
        real_ip_recursive on;

        sendfile        on;
        keepalive_timeout  300s;
        gzip on;
        gzip_types text/html text/plain text/css text/xml application/javascript font/woff font/woff2;
        gzip_comp_level 5;
        client_max_body_size 2000m;
        include /etc/nginx/conf.d/*.conf;

        server {
            listen 80 default_server;
            server_name cpweb.top www.cpweb.top;
            root /usr/share/nginx/html;

            if (-f $request_filename/index.html){
                rewrite (.*) $1/index.html break;
            }
            if (-f $request_filename/index.php){
                rewrite (.*) $1/index.php;
            }
            if (!-f $request_filename){
                rewrite (.*) /index.php;
            }
            rewrite /wp-admin$ $scheme://$host$uri/ permanent;

            location / {
                index index.php;
            }

            location ~ \.php$ {
                fastcgi_pass 127.0.0.1:9000;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params; 
            }
        }
    }

PHP:

[root@cp blog]# cat php-configmap.yaml 
apiVersion: v1
kind: ConfigMap
metadata:
  name: php-config
  namespace: blog
data:
  php.ini: |
    [PHP]
    engine = On
    short_open_tag = Off
    precision = 14
    output_buffering = 4096
    zlib.output_compression = Off
    implicit_flush = Off
    unserialize_callback_func =
    serialize_precision = -1
    disable_functions =
    disable_classes =
    zend.enable_gc = On
    zend.exception_ignore_args = On
    expose_php = On
    max_execution_time = 30
    max_input_time = 60
    memory_limit = 128M
    error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
    display_errors = Off
    display_startup_errors = Off
    log_errors = On
    log_errors_max_len = 1024
    ignore_repeated_errors = Off
    ignore_repeated_source = Off
    report_memleaks = On
    variables_order = "GPCS"
    request_order = "GP"
    register_argc_argv = Off
    auto_globals_jit = On
    post_max_size = 1024M
    auto_prepend_file =
    auto_append_file =
    default_mimetype = "text/html"
    default_charset = "UTF-8"
    doc_root =
    user_dir =
    enable_dl = Off
    file_uploads = On
    upload_max_filesize = 1024M
    max_file_uploads = 20
    allow_url_fopen = On
    allow_url_include = Off
    default_socket_timeout = 60
    [CLI Server]
    cli_server.color = On
    [Date]
    [filter]
    [iconv]
    [imap]
    [intl]
    [sqlite3]
    [Pcre]
    [Pdo]
    [Pdo_mysql]
    pdo_mysql.default_socket=
    [Phar]
    [mail function]
    SMTP = localhost
    smtp_port = 25
    mail.add_x_header = Off
    [ODBC]
    odbc.allow_persistent = On
    odbc.check_persistent = On
    odbc.max_persistent = -1
    odbc.max_links = -1
    odbc.defaultlrl = 4096
    odbc.defaultbinmode = 1
    [MySQLi]
    mysqli.max_persistent = -1
    mysqli.allow_persistent = On
    mysqli.max_links = -1
    mysqli.default_port = 3306
    mysqli.default_socket =
    mysqli.default_host =
    mysqli.default_user =
    mysqli.default_pw =
    mysqli.reconnect = Off
    [mysqlnd]
    mysqlnd.collect_statistics = On
    mysqlnd.collect_memory_statistics = Off
    [OCI8]
    [PostgreSQL]
    pgsql.allow_persistent = On
    pgsql.auto_reset_persistent = Off
    pgsql.max_persistent = -1
    pgsql.max_links = -1
    pgsql.ignore_notice = 0
    pgsql.log_notice = 0
    [bcmath]
    bcmath.scale = 0
    [browscap]
    [Session]
    session.save_handler = files
    session.use_strict_mode = 0
    session.use_cookies = 1
    session.use_only_cookies = 1
    session.name = PHPSESSID
    session.auto_start = 0
    session.cookie_lifetime = 0
    session.cookie_path = /
    session.cookie_domain =
    session.cookie_httponly =
    session.cookie_samesite =
    session.serialize_handler = php
    session.gc_probability = 1
    session.gc_divisor = 1000
    session.gc_maxlifetime = 1440
    session.referer_check =
    session.cache_limiter = nocache
    session.cache_expire = 180
    session.use_trans_sid = 0
    session.sid_length = 26
    session.trans_sid_tags = "a=href,area=href,frame=src,form="
    session.sid_bits_per_character = 5
    [Assertion]
    zend.assertions = -1
    [COM]
    [mbstring]
    [gd]
    [exif]
    [Tidy]
    tidy.clean_output = Off
    [soap]
    soap.wsdl_cache_enabled=1
    soap.wsdl_cache_dir="/tmp"
    soap.wsdl_cache_ttl=86400
    soap.wsdl_cache_limit = 5
    [sysvshm]
    [ldap]
    ldap.max_links = -1
    [dba]
    [opcache]
    [curl]
    [openssl]
    [ffi]

  www.conf: |
    [www]
    user = www-data
    group = www-data
    listen = 127.0.0.1:9000
    pm = dynamic
    pm.max_children = 5
    pm.start_servers = 2
    pm.min_spare_servers = 1
    pm.max_spare_servers = 3

执行:

[root@cp blog]# kubectl apply -f nginx-configmap.yaml -f php-configmap.yaml 
[root@cp blog]# kubectl get cm -n blog
NAME               DATA   AGE
kube-root-ca.crt   1      4h7m
nginx-config       1      6m47s
php-config         2      3m20s

6、创建 PV 和 PVC

  将网站数据解压到 /web-project/wordpress-blog/ 中,然后创建 Local 类型 PV 和 PVC,Local Storage Class 已经提前准备好了。

[root@cp blog]# kubectl get sc local 
NAME    PROVISIONER                    RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
local   kubernetes.io/no-provisioner   Delete          WaitForFirstConsumer   false                  46h

[root@cp blog]# cat wordpress-pv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: wordpress-pv
spec:
  capacity:
    storage: 10Gi
  accessModes: ["ReadWriteMany"]
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local
  local:
    path: /web-project/wordpress-blog/
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - cp
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wordpress-pvc
  namespace: blog
spec:
  accessModes: ["ReadWriteMany"]
  resources:
    requests:
      storage: 10Gi
  storageClassName: local

[root@cp blog]# kubectl apply -f wordpress-pv.yaml
[root@cp blog]# kubectl get pv wordpress-pv
NAME           CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
wordpress-pv   10Gi       RWX            Retain           Available           local                   42s
[root@cp blog]# kubectl get pvc -n blog
NAME            STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
wordpress-pvc   Pending                                      local          44s

这里还要特别注意的是,挂载的网站数据权限问题导致容器数据无法正常写入。
解决办法,基于本次迁移网站:

• 使用 Init Container 对容器进行初始化,修改挂载的网站数据权限。
• 因为使用的是 Local 卷,直接在宿主机上开放网站数据权限。
• 直接进入容器修改权限,保持应用和数据目录属主属组一致。

  这里选择用第一种方式。这里提到一个用户 www-data,这个用户一般是用于运行 web 服务的,一般很多关于 web 的容器镜像都会有这个用户,例如 nginx、httpd、tomcat、php-fpm、busybox、ubuntu 等,且 id(33)一般都是一样的。
  这样,我们可以将 nginx 和 php-fpm 运行用户都设置为 www-data,这个直接在 ConfigMap 中改。然后在后面的 Deployment 中使用 Init Container 将挂载的网站数据属主属组都设置为 www-data 即可。

7、创建 Service

[root@cp blog]# cat nginx-php-service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: nginx-php-svc
  namespace: blog
spec:
  selector:
    app: nginx-php
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80

[root@cp blog]# kubectl apply -f nginx-service.yaml 
[root@cp blog]# kubectl get svc -n blog
NAME            TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
nginx-php-svc   ClusterIP   10.1.178.68   <none>        80/TCP    6s

8、创建 Ingress

  使用 Nginx Ingress 控制器,已提前准备好,对外暴露 80 和 443。还修改了 Nginx Ingress 控制器名为 ingress-nginx-controller 的 Configmap ,在 data 块下添加了几个配置,如下:

apiVersion: v1
kind: ConfigMap
metadata:
  labels:
    helm.sh/chart: ingress-nginx-4.0.15
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.1.1
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: ingress-nginx-controller
  namespace: ingress-nginx
data:
  allow-snippet-annotations: 'true'
  compute-full-forwarded-for: "true"
  use-forwarded-headers: "true"
  proxy-body-size: "2048m"

创建 Secret 包含网站 SSL 证书:

[root@cp blog]# kubectl create secret tls cpweb.top --key cpweb.top.key --cert cpweb.top.pem -n blog
[root@cp blog]# kubectl get secrets -n blog
NAME                  TYPE                                  DATA   AGE
cpweb.top             kubernetes.io/tls                     2      11s

创建 Ingress:

[root@cp blog]# cat blog-ingress.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: cpweb.top
  namespace: blog
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - www.cpweb.top
    - cpweb.top
    secretName: cpweb.top 
  rules:
  - host: www.cpweb.top
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: nginx-php-svc
            port:
              number: 80
  - host: cpweb.top
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service: 
            name: nginx-php-svc
            port:
              number: 80

[root@cp blog]# kubectl apply -f blog-ingress.yaml 
[root@cp blog]# kubectl get ing -n blog
NAME        CLASS   HOSTS                     ADDRESS       PORTS     AGE
cpweb.top   nginx   www.cpweb.top,cpweb.top   10.60.77.22   80, 443   86s

9、部署 Nginx 和 PHP

[root@cp blog]# cat nginx-php-deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-php-deployment
  labels:
    app: nginx-php
  namespace: blog
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-php
  template:
    metadata:
      labels:
        app: nginx-php
    spec:
      initContainers:
      - name: init
        image: nginx
        command:
        - "/bin/sh"
        - "-c"
        - "chown -R www-data:www-data /usr/share/nginx/html/"
        volumeMounts:
        - name: wordpress
          mountPath: /usr/share/nginx/html/
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:
        - name: nginx-config
          mountPath: /etc/nginx/nginx.conf
          subPath: nginx.conf
        - name: wordpress
          mountPath: /usr/share/nginx/html/
      - name: php-fpm
        image: cp/php:fpm-wordpress
        ports: 
        - containerPort: 9000
        volumeMounts:
        - name: wordpress
          mountPath: /usr/share/nginx/html/
        - name: php-config
          mountPath: /usr/local/etc/php/php.ini
          subPath: php.ini
        - name: php-config
          mountPath: /usr/local/etc/php-fpm.d/www.conf
          subPath: www.conf
      volumes:
      - name: nginx-config
        configMap:
          name: nginx-config
      - name: php-config
        configMap:
          name: php-config
          items:
          - key: php.ini
            path: php.ini       
          - key: www.conf
            path: www.conf       
      - name: wordpress
        persistentVolumeClaim:
          claimName: wordpress-pvc

[root@cp blog]# kubectl get pod -n blog
NAME                                   READY   STATUS    RESTARTS   AGE
nginx-php-deployment-89dfc655b-sk42x   2/2     Running   0          34s

  因为使用了 CDN,进入 CDN 设置修改源站配置,指向新的地址。等待几分钟后,网站成功显示,测试功能都正常,至此迁移完成。

发表评论

验证码: 77 − 71 =