[TOC]

1. 说明

elk 版本:8.15.0

2. 启个 nginx

有 nginx 可以直接使用。我这里是在之前环境下 docker-compose.yml 中启动了个 nginx:

1
2
3
4
5
6
7
8
9
10
11
12
nginx:
restart: always
image: nginx:1.26.1
ports:
- "80:80"
- "443:443"
volumes:
#- ./nginx/html:/usr/share/nginx/html
- ./nginx/logs:/var/log/nginx
#- ./nginx/certs:/etc/nginx/certs
- ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/conf/conf.d:/etc/nginx/conf.d

刚开始如果没有 nginx 配置,可以先不映射配置目录和文件,先启动,然后 docker cp 把配置和目录拷贝出来,再挂载进去。

配置 nginx/conf/conf.d/default.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#
upstream kibana_servers {
server kibana:5601 weight=1 max_fails=3 fail_timeout=30s;
}

server{
listen 80;
server_name localhost;
# root /usr/local/nginx/html ;
# rewrite ^(.*) https://$server_name$1 permanent;

location / {
proxy_pass http://kibana_servers/;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_connect_timeout 30s;
proxy_read_timeout 90s;
proxy_send_timeout 90s;
client_max_body_size 50m;
client_body_buffer_size 16k; # 默认是 8k 或 16k,可以根据需要调整
}
}

3. filebeat 接入到 es

根据指引 filebeat 直接接入到 es。
fiilebeat接入

filebeat 安装参考 《Docker compose 安装 ELK》中 “6. 安装 filebeat”。

filebeat 的目录下,设置 nginx 日志路径 modules.d/nginx.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- module: nginx
# Access logs
access:
enabled: true

# Set custom paths for the log files. If left empty,
# Filebeat will choose the paths depending on your OS.
var.paths: ["/data/docker/elk/nginx/logs/access.log*"]

# Error logs
error:
enabled: true

# Set custom paths for the log files. If left empty,
# Filebeat will choose the paths depending on your OS.
var.paths: ["/data/docker/elk/nginx/logs/error.log*"]

主配置 filebeat.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
filebeat.config.modules:
# Glob pattern for configuration loading
path: ${path.config}/modules.d/*.yml

output.elasticsearch:
hosts: ["https://10.1.205.165:9200"]
username: "filebeat_writer"
password: "YOUR_PASSWORD"
ssl:
enabled: true
# fingerprint=$(openssl x509 -fingerprint -sha256 -noout -in certs/ca/ca.crt | awk -F"=" '{print $2}' | sed 's/://g')
ca_trusted_fingerprint: "33CB5A3B3ECCA59FDF7333D9XXXXXXXXFD34D5386FF9205AB8E1"
# certs/ca 目录从 es 中拷过来
certificate_authorities: ["certs/ca/ca.crt"]

# output.logstash:
# hosts: ["10.1.205.165:5044", "10.1.205.166:5044"]

setup.kibana:
host: "10.1.205.165:5601"

logging.level: warning

Dev Tools 创建角色:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
PUT /_security/role/filebeat_writer_role
{
"cluster": [
"monitor"
],
"indices": [
{
"names": [
"filebeat*"
],
"privileges": [
"create_doc",
"create",
"delete",
"index",
"write",
"all",
"auto_configure",
"manage"
],
"allow_restricted_indices": false
}
],
"applications": [],
"run_as": [],
"metadata": {},
"transient_metadata": {
"enabled": true
}
}

Dev Tools 创建用户:

1
2
3
4
5
6
7
8
POST /_security/user/filebeat_writer
{
"password" : "YOUR_PASSWORD",
"roles" : [ "filebeat_writer_role" ],
"full_name" : "Filebeat Writer",
"email" : "filebeat_writer@example.com",
"metadata" : {}
}

启用模块:

1
2
3
4
5
6
./filebeat modules enable nginx
./filebeat modules enable all-filesets

#
./filebeat setup
./filebeat -e

成功后,在 kibana 的 nginx log dashboard 中可以看到数据
有数据

4. filebeat 接入 logstash

主配置 filebeat.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
filebeat.config.modules:
# Glob pattern for configuration loading
path: ${path.config}/modules.d/*.yml

# output.elasticsearch:
# hosts: ["https://10.1.205.165:9200"]
# username: "filebeat_internal"
# password: "YOUR_PASSWORD"
# ssl:
# enabled: true
# # fingerprint=$(openssl x509 -fingerprint -sha256 -noout -in certs/ca/ca.crt | awk -F"=" '{print $2}' | sed 's/://g')
# ca_trusted_fingerprint: "33CB5A3B3ECCA59FDF7333D9XXXXXXXXFD34D5386FF9205AB8E1"
# # certs/ca 目录从 es 中拷过来
# certificate_authorities: ["certs/ca/ca.crt"]

output.logstash:
hosts: ["10.1.205.165:5044", "10.1.205.166:5044"]

setup.kibana:
host: "10.1.205.165:5601"

logging.level: warning

有问题的配置我就不贴了,最终配置在下面,注释有说明解决问题的关键。
启动 filebeat 后,在 kibana Dev Tools 中查询:

1
2
3
4
5
6
7
GET filebeat-*/_search
{
"size": 1,
"sort": [
{}
]
}

结果发现 ingest pipeline 处理异常,经过一段时间排查,找到了 相同问题链接
结果

然后下面是最终的可用 Logstash Pipeline 设置 logstash/pipeline/filebeat.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#
input {
beats {
port => 5044
}
}

filter {
if [@metadata][pipeline] {
# 这里解决上面 `[event][original]` 已存在问题
ruby {
code => "event.remove('[event][original]')"
}
} else if [log_type] == "k8s" {
# 将嵌套字段提升为顶层字段
mutate {
rename => {
"[kubernetes][namespace]" => "kubernetes_namespace"
"[kubernetes][node][name]" => "kubernetes_node_name"
"[kubernetes][pod][ip]" => "kubernetes_pod_ip"
"[kubernetes][pod][name]" => "kubernetes_pod_name"
"[container][runtime]" => "container_runtime"
}
}

# 使用 prune 过滤器保留所需字段
prune {
whitelist_names => ["@timestamp", "log_type", "container_runtime", "kubernetes_namespace", "kubernetes_node_name", "kubernetes_pod_ip", "kubernetes_pod_name", "message"]
}
}
}

output {
if [@metadata][pipeline] {
elasticsearch {
hosts => ["https://es01:9200", "https://es02:9200", "https://es03:9200"]
user => "logstash_writer"
password => "logstash_password"
ssl_enabled => true
ssl_certificate_authorities => "/usr/share/logstash/certs/ca/ca.crt"
action => "create"
pipeline => "%{[@metadata][pipeline]}"
index => "%{[@metadata][beat]}-%{[@metadata][version]}"
manage_template => false
}
} else if [log_type] == "k8s" {
elasticsearch {
hosts => ["https://es01:9200", "https://es02:9200", "https://es03:9200"]
index => "k8s-%{+YYYY.MM.dd}"
user => "logstash_writer"
password => "logstash_password"
ssl_enabled => true
ssl_certificate_authorities => "/usr/share/logstash/certs/ca/ca.crt"
}
} else {
# 默认索引
elasticsearch {
hosts => ["https://es01:9200", "https://es02:9200", "https://es03:9200"]
index => "logstash-%{+YYYY.MM.dd}"
user => "logstash_writer"
password => "logstash_password"
ssl_enabled => true
ssl_certificate_authorities => "/usr/share/logstash/certs/ca/ca.crt"
action => "create"
manage_template => false
}
}
}

logstash 角色和用户权限同上面 filebeat 角色和用户。

5. 总结

Filebeat 收集 nginx、mysql 等日志,没有特别需求,最好直接接入 ES 中,少了一层 logstash 其实性能更好,问题也相对较少,官方文档推荐的。

参考资料:
[1] https://www.elastic.co/guide/en/logstash/8.15/use-ingest-pipelines.html
[2] https://www.elastic.co/guide/en/elasticsearch/reference/8.15/ingest.html
[3] https://www.elastic.co/guide/en/beats/filebeat/8.15/filebeat-installation-configuration.html
[4] https://discuss.elastic.co/t/aws-ingest-pipeline-error-in-processor-rename-message-to-event-original/341472/8
[5] https://www.elastic.co/guide/en/ecs-logging/overview/master/intro.html

1. 环境说明

环境 A: * jenkins 版本:2.253 * 使用 systemctl 管理的 jenkins 服务

环境 B: 可以上网的机器,装有 docker-compose

docker 和 docker-compose 安装,这里都略了。

2. 安装旧版本

2.1 环境 A jenkins 目录打包文件

打包需要的文件放在 /tmp 目录下待下载:

1
2
3
/home/jenkins/  # jenkins 数据目录
tar -cvf jenkins.tar --exclude='caches' --exclude='jobs' --exclude='logs' --exclude='nodes' --exclude='workspace' *
mv jenkins.tar /tmp/

然后把 jenkins.tar 下载,并传输到 B 环境机器上。

2.2 环境 B 机器安装旧版本 jenkins

在 B 环境机器上,上传下载的 jenkins.tar/tmp/ 下,解压准备好 jenkins_home

1
2
3
4
5
mkdir -p /data/tmp/jenkins/data
cd /data/tmp/jenkins/data
tar -xvf /tmp/jenkins.tar
cd ../..
chown 1000.1000 -R jenkins/

使用 docker-compose 安装旧版本 jenkins。

/data/tmp/docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
version: "3.1"
networks:
app:

services:
jenkins:
image: harbor.ygqygq2.com/proxy/jenkins/jenkins:2.253-centos7
restart: always
# user: root
networks:
- app
environment:
- TZ=Asia/Shanghai
- JAVA_OPTS=-Duser.timezone=Asia/Shanghai
- JENKINS_ARGS="--sessionTimeout=86400"
volumes:
- ./jenkins/sudoers.d:/etc/sudoers.d
- ./jenkins/data:/var/jenkins_home
#- ./jenkins/war:/usr/share/jenkins
- ./jenkins/caches:/caches
- /var/run/docker.sock:/var/run/docker.sock
ports:
- "8090:8080"

启动,并浏览器登录 B 环境,注意端口

1
2
3
cd /data/tmp
docker-compose up -d
docker-compose ps

使用环境 A 的 jenkins 管理员用户密码,在浏览器登录后,滑到最底部查看 jenkins 版本,看是否和旧版本一致。

3. 环境 B jenkins 升级插件

前面登录 jenkins 后,如果版本和旧版本一致,则成功启动。进入插件升级管理界面,先选择 “全选” 插件升级,但不勾选 “安装完成后重启 Jenkins”。

然后编辑 /data/tmp/docker-compose.yml,修改镜像 tag 为当前最新稳定版,比如当前为 2.452.3-lts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
version: "3.1"
networks:
app:

services:
jenkins:
image: harbor.ygqygq2.com/proxy/jenkins/jenkins:2.452.3-lts
restart: always
# user: root
networks:
- app
environment:
- TZ=Asia/Shanghai
- JAVA_OPTS=-Duser.timezone=Asia/Shanghai
- JENKINS_ARGS="--sessionTimeout=86400"
volumes:
- ./jenkins/sudoers.d:/etc/sudoers.d
- ./jenkins/data:/var/jenkins_home
#- ./jenkins/war:/usr/share/jenkins
- ./jenkins/caches:/caches
- /var/run/docker.sock:/var/run/docker.sock
ports:
- "8090:8080"

重启容器:

1
2
3
cd /data/tmp
docker-compose up -d
docker-compose ps

4. 环境 B jenkins 问题处理

docker logs -f 查看日志,并且看浏览器是否能访问和登录。

  1. 如果遇到访问和登录问题,一般是插件不兼容,查看日志,把不兼容的插件文件从 /data/tmp/jenkins/data/plugins/ 插件目录中移出去,并且使用 docker 命令手动重启容器,直到能登录并且能更新插件为止;
  2. 根据移出去的插件名再从插件管理界面搜索安装;
  3. 最后把插件再升级到兼容版本最新;
  4. 看看插件管理界面有什么提示和未启动的插件原因;

比如: 弃用插件

插件不兼容

无法安装

因为 LTS 的版本更新较慢,插件是跟着最新版本的,有时候会不兼容 LTS 版本。所以,使用 war 包方式将 jenkins 升级。

先把原 war 包目录拷出来备份

1
2
# 56 是 docker ps 看到的容器 id
docker cp 56:/usr/share/jenkins jenkins/war_bak

下载最新版本 jenkins war 包到 /data/tmp/jenkins/war/

1
2
3
mkdir -p /data/tmp/jenkins/war/ref
wget https://updates.jenkins.io/download/war/2.467/jenkins.war -O /data/tmp/jenkins/war/jenkins.war
chown 1000.1000 -R /data/tmp/jenkins/war/

取消 /data/tmp/docker-compose.yml 中 war 目录那行的注释,让最新 war 包挂载进去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
version: "3.1"
networks:
app:

services:
jenkins:
image: harbor.ygqygq2.com/proxy/jenkins/jenkins:2.452.3-lts
restart: always
# user: root
networks:
- app
environment:
- TZ=Asia/Shanghai
- JAVA_OPTS=-Duser.timezone=Asia/Shanghai
- JENKINS_ARGS="--sessionTimeout=86400"
volumes:
- ./jenkins/sudoers.d:/etc/sudoers.d
- ./jenkins/data:/var/jenkins_home
- ./jenkins/war:/usr/share/jenkins
- ./jenkins/caches:/caches
- /var/run/docker.sock:/var/run/docker.sock
ports:
- "8090:8080"

重启容器:

1
2
3
cd /data/tmp
docker-compose up -d
docker-compose ps

这时候基本没什么插件问题了,把弃用的插件卸载。

卸载弃用插件

后面就是流水线和设置等的调整测试了。

5. 升级 A 环境 jenkins

5.1 升级 A 环境 jenkins master

环境 B 机器打包 jenkins 目录

1
2
cd /data/tmp/
tar -cvf jenkins.tar jenkins/

然后下载 jenkins.tar 上传到环境 A 机器 /tmp

因为前面环境 A 机器打包过滤了这些目录,这些为任务、节点和工作目录,所以新版本的 jenkins 目录,把这几个目录拷贝过来先放好。--exclude='caches' --exclude='jobs' --exclude='logs' --exclude='nodes' --exclude='workspace'

1
2
3
4
5
6
7
8
mkdir -p /data/docker/jenkins/data
rsync -avz /home/jenkins/{caches,jobs,logs,nodes,workspace} /data/docker/jenkins/data/

# 解压 /tmp/jenkins.tar
cd /data/docker/
tar -xvf /tmp/jenkins.tar

chown 1000.1000 -R /data/docker/jenkins

在 docker-compose 配置中添加上 jenkins 相关配置,注意这里使用的是 8090 端口,目的是不影响现有的 jenkins 8080 端口,等 8090 测试 ok 后,再关掉原来的 8080 端口,替换 8090 成 8080 端口完成升级。 /data/docker/docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
version: "3.1"
networks:
app:

services:
jenkins:
image: harbor.ygqygq2.com/proxy/jenkins/jenkins:2.452.3-lts
restart: always
# user: root
networks:
- app
environment:
- TZ=Asia/Shanghai
- JAVA_OPTS=-Duser.timezone=Asia/Shanghai
- JENKINS_ARGS="--sessionTimeout=86400"
volumes:
- ./jenkins/sudoers.d:/etc/sudoers.d
- ./jenkins/data:/var/jenkins_home
- ./jenkins/war:/usr/share/jenkins
- ./jenkins/caches:/caches
- /var/run/docker.sock:/var/run/docker.sock
ports:
- "8090:8080"

镜像传到环境 A 机器可以通过导出导入方式,或者传到内网镜像仓库,从内网镜像仓库拉,目的是保证环境 A 机器上有这个最新的 jenkins 镜像。

启动容器:

1
2
3
cd /data/tmp
docker-compose up -d
docker-compose ps

报错 报错

原因是 docker 版本较旧,升级 docker 版本,或者增加参数:

1
2
security_opt:
- seccomp:unconfined

现在应该可以启动了。

5.2 升级 A 环境 jenkins node

登录 jenkins 后,可以看到 Node 已经连不上。

先更新 node 的 jdk,更新为 openjdk17下载链接

环境变量

1
2
3
4
JAVA_HOME=/opt/java/jdk
PATH=$JAVA_HOME/bin:$PATH
CLASSPATH=.:$JAVA_HOME/lib/tools.jar
export PATH JAVA_HOME CLASSPATH

jdk 放好

1
2
3
4
5
cd /tmp
wget https://builds.openlogic.com/downloadJDK/openlogic-openjdk/17.0.11+9/openlogic-openjdk-17.0.11+9-linux-x64.tar.gz
tar -zxvf openlogic-openjdk-17.0.11+9-linux-x64.tar.gz
mv /tmp/openlogic-openjdk-17.0.11+9-linux-x64 /opt/java/openjdk-17.0.11
ln -s /opt/java/openjdk-17.0.11 /opt/java/jdk

然后节点设置指定这个 jdk 就可以启动了。

因为我们 jenkins master 是使用 docker 方式启动的,流水线中不少步骤还是使用原来的宿主机。我们添加一个 agent,宿主机作为 master agent,而真正的 master 则不添加标签,以此作为过度阶段。

比如 宿主节点

  • 通过在 Jenkins 的管理界面中,进入“系统配置”或“全局安全配置”;
  • 确保已经启用了代理的安全配置,比如“TCP 端口用于 JNLP 代理”选项不是设置为“禁用”;
  • 使用固定端口 50000,因为 docker-compose 也得暴露出来;
  • 这个 local-host 节点的标签设置为 master;
  • Built-In Node 节点的标签设置为空;

在使用 ssh 的地方,还有个安全设置要注意,比如使用 ssh 方式检出代码,如果不设置成 “No verification” 可能会导致无法检出代码。 ssh host key

6. 小结

Jenkins 升级涉及到的东西较多,用的插件、功能越多,流水线依赖越多,升级难度越大。 Jenkins 方案方式的变化也会引起流水线使用的变化,比如很多原来直接使用 master 运行的部分,随着 master 的容器化,里面很多命令和工具都没了,这部分要权衡好,当然像我上面一样将宿主机作为 master 标签也是一个办法。 Jenkins 升级后还需要一段时间的磨合,你可能发现某个功能的写法的变化,某个功能因为安全原因需要增加额外设置或者按安全要求修改。 当然,容器化隔离提供了更安全的环境,各部分使用不同的镜像做到各司其职是更好的实践。


title: 一套 shell 脚本管理自签证书 tags: openssl, ca, certification author: Chinge Yang date: 2024-7-10


[TOC]

1. 需求

当前 ssl 证书越来越普及,很多时候,开发时使用 http 是没有问题的,但是上生产时使用 https 就可能遇到问题,为了在开发阶段就处理好 https 请求问题,推荐使用自签证书作为测试证书或者作为服务器间内网证书。原来的 openssl v1 证书已经不被浏览器所信任了,需要使用 v3 extension 来扩展证书信息。

2. 自签证书工具

比较推荐 KeyManager,图形化工具,虽然不能自定义很多证书信息,但是管理签发证书比较方便。

其它的一些工具: mkcert xca

3. shell 脚本作为 CA 证书中心

我自己也写了套 ca 证书管理的 shell 脚本,使用和配置都非常简单。 链接:openssl-ca-ceneter

简单介绍:

功能: * 生成 CA 证书 * 生成 server 证书 * 生成 client 证书 * 生成 email 签名证书 * 验证证书 * 使用 csr 签发证书

使用说明:

  • rhel 系目录为 /etc/pki/tls
  • ubuntu 目录为 /etc/ssl

备份 /etc/pki/tls,然后把整个仓库脚本和 openssl.cnf 放进去就可以使用了。

1
2
3
4
new_ca.sh  # 生成 CA 证书,注意里面有个 exit 防误操作
new_client.sh # 生成客户端证书
new_email.sh # 生成邮件验证证书
new_server.sh # 针对域名等生成服务器证书

首先生成一个自己的 CA 证书

1
/bin/bash new_ca.sh

服务器证书用的最多,可支持多个证书,包括通配符证书

1
/bin/bash new_server.sh www.domainA.com '*.domainB.com'

生成的证书在 certs 目录下,私钥在 private 目录下。 比如 certs/ygqygq2.com.crt 有 Certificate Details 内容的证书文件 private/ygqygq2.com.key 私钥文件 certs/ygqygq2.com-fullchain.crt 有 CA 证书的证书链文件 certs/ygqygq2.com.pem 纯文本的 pem 证书文件

在配置比如 nginx 证书时,推荐使用证书链文件作为证书。

参考资料: [1] https://www.openssl.org/docs/manmaster/man5/x509v3\_config.html

1. 背景

最近 docker hub 下载镜像经常失败,同一台机器有可能这个镜像可以,那个镜像不行。另外,看到很多人安装 kubesphere/kubernetes 不知道如何处理镜像拉取失败问题。

2. 基本概念和基本操作

2.1 vim 基本操作

vim 基本操作,以下为按键流程说明,比如: * 前后左右用方向键即可,或者按 esc 状态下,h(左) j(下) k(上) l(右) * 查找 “image:”,ecs -> /image: -> 回车 * 往后查找,前面查找按了回车后,按 n * 往前查找,前面查找按了回车后,按 N(shift + n) * 替换 aaa 成 bbb,esc -> 输入 :s@aaa@bbb@g (注意冒号)g 为全局替换,不加 g 则替换匹配到的第一个

基本能满足编辑需求了。

2.2 kubernetes 资源编辑

kubernetes 中最常见拉镜像的有: deployment:kubectl 命令使用时简写:deploy statefulset:kubectl 命令使用时简写:sts daemonset:kubectl 命令使用时简写:ds

比如编辑一个 deployment abc,如果不是在 default 命名空间,则需要接上命令空间: kubectl edit deploy abc [-n NAMESPACE]

命令执行后,就是 vim 操作了。

镜像拉取策略: * imagePullPolicy: "IfNotPresent":有相同地址和 tag 的镜像存在时则不拉取 * imagePullPolicy: "Always":总是拉取镜像,即使存在相同地址和 tag 镜像时

3. kubesphere 安装设置私有仓库

3.1 私有仓库 harbor 说明

我的私有仓库是 harbor.ygqygq2.com,带宽小,勉强能用。其中 library 项目中保留的为 创建 issue 推送上去的镜像地址,对应关系是: * nginx:latest 对应 harbor.ygqygq2.com/library/nginx:latest * bitnami/nginx:latest 对应 harbor.ygqygq2.com/library/bitnami/nginx:latest

其中 proxy 项目为代理 docker hub,对应关系是: * nginx:latest 对应 harbor.ygqygq2.com/proxy/library/nginx:latest * bitnami/nginx:latest 对应 harbor.ygqygq2.com/proxy/bitnami/nginx:latest

私有仓库 harbor 推荐的做法就是 library 设置成公开,然后里面镜像保留原镜像目录结构,像我上面一样。

3.2 config-sample.yaml 先用默认的

kubekey 安装前设置环境变量 export KKZONE=cn

然后 config-sample.yaml 使用默认的:

1
2
3
4
5
registry:
privateRegistry: "" # 私有仓库地址,如果镜像都在 library 中,则比如 harbor.ygqygq2.com/library
namespaceOverride: ""
registryMirrors: []
insecureRegistries: [] # 如果私有仓库为 http 访问,得加到这里

kubekey 安装 kubernetes 集群,初始化时就可以使用命令查看容器运行情况了。 * docker 环境推荐 docker 命令,比如 docker ps -a * containerd 环境推荐 nerdctl 命令,只是注意镜像拉取要指定命令空间 --namespace k8s.io,可以设置 alias nerdctl=nerdctl --namespace k8s.io,比如 nerdctl ps -a --namespace k8s.io

注意 sandbox_image 镜像地址: * docker 环境中查看 kubelet 配置 /etc/systemd/system/kubelet.service.d/10-kubeadm.conf,可以看到 EnvironmentFile=-/etc/default/kubelet,所以可以配置 /etc/default/kubelet(Ubuntu)、/etc/sysconfig/kubelet(RHEL系),内容为:KUBELET_EXTRA_ARGS="--pod-infra-container-image="registry.cn-beijing.aliyuncs.com/kubesphereio/pause:3.9" * containerd 配置中可以配置为,sandbox_image = "registry.cn-beijing.aliyuncs.com/kubesphereio/pause:3.9"

安装完 kubernetes 后,kubectl get pod -A 可以查看 pod 运行情况,此时就可以根据 4 解决失败的 POD 镜像问题了。

3.3 kubesphere-installer.yaml

kubesphere-installer.yaml 中有个镜像地址,修成成你的仓库镜像地址,也可以使用我的仓库image: harbor.ygqygq2.com/proxy/kubesphere/ks-installer:v3.4.1,并注意修改策略 imagePullPolicy: "IfNotPresent"

installer 地址修改

1
2
kubectl apply -f kubesphere-installer.yaml
kubectl get pod -A # 查看 pod 情况

有pod异常

kubectl describe pod ks-installer-67f67f9698-lglwb -n kubesphere-system

没有镜像

因为我们使用 yaml 安装的,直接修改 yaml,再 kubectl apply -f kubesphere-installer.yaml 直接修改 yaml

再次查看 pod,等待它运行正常。 再次查看

3.4 cluster-configuration.yaml

使用私有仓库地址

1
2
3
4
kubectl apply -f cluster-configuration.yaml 
kubectl get pod -A

kubectl logs -f <ks-installer pod名> -n kubesphere-system --tail=10

查看日志

kubekey 安装ks 会有默认 storageclass,但是使用 ks-installer 安装,因为没有默认 storageclass 可能日志提示 "msg": "Default StorageClass was not found !",创建一个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
cat > local-sc.yaml<<EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
annotations:
cas.openebs.io/config: |
- name: StorageType
value: "hostpath"
- name: BasePath
value: "/var/openebs/local"
openebs.io/cas-type: local
storageclass.beta.kubernetes.io/is-default-class: "true"
storageclass.kubesphere.io/supported-access-modes: '["ReadWriteOnce"]'
name: local
provisioner: openebs.io/local
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer

---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
name: openebs-localpv-provisioner
openebs.io/component-name: openebs-localpv-provisioner
openebs.io/version: 3.3.0
name: openebs-localpv-provisioner
namespace: kube-system
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: openebs-localpv-provisioner
openebs.io/component-name: openebs-localpv-provisioner
strategy:
type: Recreate
template:
metadata:
labels:
name: openebs-localpv-provisioner
openebs.io/component-name: openebs-localpv-provisioner
openebs.io/version: 3.3.0
spec:
containers:
- env:
- name: NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
- name: OPENEBS_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: OPENEBS_SERVICE_ACCOUNT
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.serviceAccountName
- name: OPENEBS_IO_ENABLE_ANALYTICS
value: "true"
- name: OPENEBS_IO_INSTALLER_TYPE
value: openebs-operator-lite
- name: OPENEBS_IO_HELPER_IMAGE
value: openebs/linux-utils:3.3.0
# image: harbor.ygqygq2.com/proxy/openebs/provisioner-localpv:3.3.0
image: openebs/provisioner-localpv:3.3.0
imagePullPolicy: IfNotPresent
livenessProbe:
exec:
command:
- sh
- -c
- test 0 = 1
failureThreshold: 3
initialDelaySeconds: 30
periodSeconds: 60
successThreshold: 1
timeoutSeconds: 1
name: openebs-provisioner-hostpath
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: openebs-maya-operator
serviceAccountName: openebs-maya-operator
terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: openebs-maya-operator
namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: openebs-maya-operator
rules:
- apiGroups: ["*"]
resources: ["nodes", "nodes/proxy"]
verbs: ["*"]
- apiGroups: ["*"]
resources: ["namespaces", "services", "pods", "pods/exec", "deployments", "deployments/finalizers", "replicationcontrollers", "replicasets", "events", "endpoints", "configmaps", "secrets", "jobs", "cronjobs"]
verbs: ["*"]
- apiGroups: ["*"]
resources: ["statefulsets", "daemonsets"]
verbs: ["*"]
- apiGroups: ["*"]
resources: ["resourcequotas", "limitranges"]
verbs: ["list", "watch"]
- apiGroups: ["*"]
resources: ["ingresses", "horizontalpodautoscalers", "verticalpodautoscalers", "poddisruptionbudgets", "certificatesigningrequests"]
verbs: ["list", "watch"]
- apiGroups: ["*"]
resources: ["storageclasses", "persistentvolumeclaims", "persistentvolumes"]
verbs: ["*"]
- apiGroups: ["apiextensions.k8s.io"]
resources: ["customresourcedefinitions"]
verbs: [ "get", "list", "create", "update", "delete", "patch"]
- apiGroups: ["openebs.io"]
resources: [ "*"]
verbs: ["*"]
- nonResourceURLs: ["/metrics"]
verbs: ["get"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: openebs-maya-operator
subjects:
- kind: ServiceAccount
name: openebs-maya-operator
namespace: kube-system
roleRef:
kind: ClusterRole
name: openebs-maya-operator
apiGroup: rbac.authorization.k8s.io
EOF

kubectl apply -f local-sc.yaml

安装 openelb localpath storageclass,就会有镜像拉取问题了。所以你现在就可以尝试解决了。

openelb init

查看问题,可以看出来 init-pvc 的 pod 并不属于 deploy 啥的,它只是一个 pod, 查看问题

所以直接编辑 pod,因为也没有 deploy/sts/ds 给你编辑 init-pvc, 编辑pod

可以看到运行成功 正常完成

其它 2 个 pod 同理处理。如果想 pod 快速重启,直接删除 pod 即可,它自动会重启。

重启 pod

还在卡住

kubectl describe pod -n kubesphere-system openldap-0 看是什么状态,发现是 pvc 还没准备好。 查原因

查 localpv provisioner pod 日志 kubectl logs -f -n kube-system openebs-localpv-provisioner-7bbb56b778-mwrp5 日志 看来是 init pod存在

尝试删除 init-pvc pod 删除 pod

再次看日志 看到成功日志 pvc成功创建

看ks-installer pod 日志 看ks-installer pod 日志

如果 ks-installer POD 没有动作,你又想它执行任务,可以删除 ks-installer POD,kubectl delete pod <ks-installer pod名> -n kubesphere-system,它重启后,就会执行任务了。 重启ks-installer pod

看安装情况 查看pod

发现问题,重试几次都卡在这 重试几次都在这

把 minio 关了,删除它的 pvc 设置 pod为0 删除pvc

尝试恢复 pod,但是还是一样 恢复pod

helm list -n kubesphere-system 查看是否安装成功 minio,看到没安装成功 没有安装

进 ks-installer 手动安装,安装失败,发现其实只是没显示出来,安装名字可以看看正常集群是什么。uninstall 后,重启 ks-installer安装。 进ks-installer手动安装

查看日志,minio 安装成功,已经跳过了。 minio已经安装成功

继续看 pod 和日志 继续看pod

又有镜像拉取问题 发现又有镜像拉取问题

发现是 busybox 镜像,这个我提过 issue busybox镜像

kubectl edit sts -n kubesphere-logging-system opensearch-cluster-master 改它

1
2
image: harbor.ygqygq2.com/proxy/busybox:latest
imagePullPolicy: IfNotPresent

后面镜像问题处理基本前面已经出现过,都能处理了。

4. 总结镜像失败处理

4.1 直接编辑资源 deploy、sts、ds 等

  • kubectl get pod -A 找一下有什么失败的 POD
  • kubectl describe pod <POD 名> [-n namespace] 查看 pod 状态
  • 修改资源 deploy/sts/ds 的镜像地址和拉取策略

4.2 无法修改资源修改镜像地址和拉取策略时

这种一般是程序(比如 ks)封装了,你虽然修改资源可以拉取镜像,但是它还是可能被恢复,那只能使用 tag 镜像方式,比如前面遇到的的 initContainers 使用 busybox:latestimagePullPolicy: "Always",如果镜像拉取策略无法被修改,这更麻烦。推荐命令有前面的 docker/nerdctl

在 pod 所在节点

1
2
nerdctl pull harbor.ygqygq2.com/proxy/library/busybox:latest --namespace k8s.io
nerdctl tag harbor.ygqygq2.com/proxy/library/busybox:latest busybox:latest --namespace k8s.io

然后就等 pod 自动重启。

4.3 helm chart 修改镜像仓库地址

推荐使用 bitnami/charts,写的很规范,因为它们都可以通过 values.yaml 中的 global.imageRegistry 设置镜像仓库地址,比如我的镜像都在 library 中,而且保持原镜像目录构建,则可以设置 global.imageRegistryharbor.ygqygq2.com/library

全局仓库地址

如果遇到不规范的 charts,而且它引用其它的 charts,可以从主 values.yaml 设置子 charts 的镜像地址设置,也可以直接修改子 charts 目录下的 values.yaml 来设置。

在使用 helm charts 安装应用时,推荐使用 helm pull 到本地安装,比如:

1
2
3
4
5
6
7
8
9
helm repo add kubesphere https://charts.kubesphere.io/stable
helm repo update
mkdir -p /data/helm # 专门建个目录管理安装的 helm charts
cd /data/helm
helm pull --untar kubesphere/openelb # 拉到本地
cd openelb
mkdir kube-system # 将要安装在这个命名空间下
rsync -avz ../values.yaml . # 不修改原 values.yaml,因为可能还要作参考
helm install openelb -f values.yaml ../ -n kube-system

1. 准备环境

版本: kubernetes: v1.29.2 kubesphere: v3.4.1 kubekey: v3.1.1

说明:

  • kubekey 只用于安装 kubernetes,因为 kubesphere 的配置在安装时经常需要变动,用 ks-installer 的 yaml 文件更好管理;
  • ks-installer 用于安装 kubesphere,kubekey、ks-installer 分工明确;
  • 本文在已有 harbor 仓库环境下,建议把镜像都放在公开仓库 library 中,如没有 harbor 仓库,按官方文档来即可;

1.1 机器准备

4 台机器,操作系统:Ubuntu 24.04/RHEL8/CentOS9

1
2
3
4
5
10.0.0.130 k8smaster-lv01
10.0.0.131 k8snode-lv01
10.0.0.132 k8snode-lv02
10.0.0.133 k8snode-lv03
10.0.0.140 lb.kubesphere.local

1.2 离线安装包准备

1
2
## kk create manifest --with-kubernetes v1.29.2
# kk create manifest # 最好在已有集群创建

这里提供一个可以直接使用的,因为系统安装包,我们自己处理,所以这里不用管系统的 iso 啥的地址。 manifest-sample.yaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
apiVersion: kubekey.kubesphere.io/v1alpha2
kind: Manifest
metadata:
name: sample
spec:
arches:
- amd64
operatingSystems:
- arch: amd64
type: linux
id: red
version: "Can't get the os version. Please edit it manually."
osImage: Red Hat Enterprise Linux 8.10 (Ootpa)
repository:
iso:
localPath:
url:
- arch: amd64
type: linux
id: red
version: "Can't get the os version. Please edit it manually."
osImage: Red Hat Enterprise Linux 8.9 (Ootpa)
repository:
iso:
localPath:
url:
kubernetesDistributions:
- type: kubernetes
version: v1.29.2
components:
helm:
version: v3.14.3
cni:
version: v1.2.0
etcd:
version: v3.5.13
containerRuntimes:
- type: containerd
version: 1.7.13
calicoctl:
version: v3.27.3
crictl:
version: v1.29.0

images:
- docker.io/aledbf/kube-keepalived-vip:0.35
- docker.io/bitnami/etcd:3.5.6-debian-11-r10
- docker.io/bitnami/kubectl:1.29.2
- docker.io/calico/cni:v3.27.3
- docker.io/calico/node:v3.27.3
- docker.io/coredns/coredns:1.9.3
- docker.io/grafana/promtail:2.8.3
- docker.io/kubesphere/examples-bookinfo-reviews-v1:1.16.2
- docker.io/kubesphere/fluent-bit:v1.9.4
- docker.io/kubesphere/k8s-dns-node-cache:1.22.20
- docker.io/kubesphere/ks-apiserver:v3.4.1
- docker.io/kubesphere/ks-installer:v3.4.1
- docker.io/kubesphere/ks-installer:v3.4.1-patch.0
- docker.io/kubesphere/ks-jenkins:v3.4.0-2.319.3-1
- docker.io/kubesphere/kube-apiserver:v1.29.2
- docker.io/kubesphere/kube-controller-manager:v1.29.2
- docker.io/kubesphere/kube-proxy:v1.29.2
- docker.io/kubesphere/kube-rbac-proxy:v0.11.0
- docker.io/kubesphere/kube-scheduler:v1.29.2
- docker.io/kubesphere/pause:3.9
- docker.io/library/busybox:latest
- docker.io/openebs/lvm-driver:1.5.0
- docker.io/openebs/mayastor-agent-ha-node:v2.6.1
- docker.io/openebs/mayastor-csi-node:v2.6.1
- docker.io/openebs/mayastor-io-engine:v2.6.1
- docker.io/openebs/zfs-driver:2.5.0
- docker.io/opensearchproject/opensearch:2.6.0
- docker.io/osixia/openldap:1.3.0
- docker.io/prom/node-exporter:v1.3.1
- docker.io/weaveworks/scope:1.13.0
- quay.io/argoproj/argocd:v2.3.3
- registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.10.0
- registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.8.0
registry:
auths: {}
1
2
# 生成离线安装包
kk artifact export -m manifest-sample.yaml -o kubesphere.tar.gz

因为最近 docker hub 无法在大陆拉 image,可以使用我这个 Jenkinsfile 配合海外节点将 image 拉下来,传到私有 harbor 仓库。 mirror-images-to-harbor,当前直接在它上面提 issue 同步也可,只是一个 issue 只能同步一个 image。

当然,官方的这个 image 同步脚本 offline-installation-tool.sh 也可以,它是直接拉的 image,可能拉不动,你下面列表里的 image 得转成你拉得动的 image 地址:

以下为 image 列表,生成的 image 列表,并不完全,所以后面发现有漏的 image,随时使用 jenkins 把缺少的 image 同步下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
docker.io/aledbf/kube-keepalived-vip:0.35
docker.io/bitnami/etcd:3.5.6-debian-11-r10
docker.io/bitnami/kubectl:1.29.2
docker.io/calico/cni:v3.27.3
docker.io/calico/node:v3.27.3
docker.io/coredns/coredns:1.9.3
docker.io/grafana/promtail:2.8.3
docker.io/kubesphere/examples-bookinfo-reviews-v1:1.16.2
docker.io/kubesphere/fluent-bit:v1.9.4
docker.io/kubesphere/k8s-dns-node-cache:1.22.20
docker.io/kubesphere/ks-apiserver:v3.4.1
docker.io/kubesphere/ks-installer:v3.4.1
docker.io/kubesphere/ks-installer:v3.4.1-patch.0
docker.io/kubesphere/ks-jenkins:v3.4.0-2.319.3-1
docker.io/kubesphere/kube-apiserver:v1.29.2
docker.io/kubesphere/kube-controller-manager:v1.29.2
docker.io/kubesphere/kube-proxy:v1.29.2
docker.io/kubesphere/kube-rbac-proxy:v0.11.0
docker.io/kubesphere/kube-scheduler:v1.29.2
docker.io/kubesphere/pause:3.9
docker.io/library/busybox:latest
docker.io/openebs/lvm-driver:1.5.0
docker.io/openebs/mayastor-agent-ha-node:v2.6.1
docker.io/openebs/mayastor-csi-node:v2.6.1
docker.io/openebs/mayastor-io-engine:v2.6.1
docker.io/openebs/zfs-driver:2.5.0
docker.io/opensearchproject/opensearch:2.6.0
docker.io/osixia/openldap:1.3.0
docker.io/prom/node-exporter:v1.3.1
docker.io/weaveworks/scope:1.13.0
quay.io/argoproj/argocd:v2.3.3
registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.10.0
registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.8.0

1.3 安装依赖和配置

系统源即可安装的依赖包,所有节点都需要执行

Ubuntu 系:

1
apt-get install -y socat conntrack ebtables ipset chrony

RHEL 系:

1
yum install -y socat conntrack ebtables ipset chrony

因为使用私有 Harbor 仓库,所有节点配置 host:

10.25.23.102 harbor.ops.shenzhen.com

1.4 安装负载均衡 keepalived + haproxy

负载均衡节点安装 keepalived haproxy

1
2
apt-get -y install keepalived haproxy
# yum -y install keepalived haproxy

新建用户

1
useradd -r -u 139 -g 100 -s /sbin/nologin keepalived_script

配置 keepalived /etc/keepalived/keepalived.conf (注意配置中注释,不同节点权重不同)内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
! /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
script_user keepalived_script
enable_script_security
router_id LVS_DEVEL
max_auto_priority 99
}
vrrp_script check_apiserver {
script "/etc/keepalived/check_apiserver.sh"
interval 3
weight -2
fall 10
rise 2
}

vrrp_instance VI_1 {
# 只配置一个 MASTER,其它的配置为 BACKUP
state MASTER
# 注意网卡名
interface ens192
virtual_router_id 60
# MASTER 权重最高,尽量全部设置为不同的权重
priority 101
authentication {
auth_type PASS
auth_pass k8s
}
virtual_ipaddress {
10.0.0.140
}
track_script {
check_apiserver
}
}

/etc/keepalived/check_apiserver.sh 内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash

# if check error then repeat check for 12 times, else exit
err=0
for k in $(seq 1 12)
do
check_code=$(curl -k https://localhost:6443)
if [[ $check_code == "" ]]; then
err=$(expr $err + 1)
sleep 5
continue
else
err=0
break
fi
done

if [[ $err != "0" ]]; then
# if apiserver is down send SIG=1
echo 'apiserver error!'
exit 1
else
# if apiserver is up send SIG=0
echo 'apiserver normal!'
exit 0
fi

chmod a+x /etc/keepalived/check_apiserver.sh

配置 haproxy /etc/haproxy/haproxy.cfg 内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
global
log 127.0.0.1 local0 err
maxconn 50000
uid 138
gid 138
#daemon
pidfile haproxy.pid

defaults
mode http
log 127.0.0.1 local0 err
maxconn 50000
retries 3
timeout connect 5s
timeout client 30s
timeout server 30s
timeout check 2s

listen admin_stats
mode http
bind 0.0.0.0:1080
log 127.0.0.1 local0 err
stats refresh 30s
stats uri /haproxy-status
stats realm Haproxy\ Statistics
stats auth admin:k8s
stats hide-version
stats admin if TRUE

frontend k8s-https
bind 0.0.0.0:8443
mode tcp
#maxconn 50000
default_backend k8s-https

backend k8s-https
mode tcp
balance roundrobin
server master1 10.0.0.130:6443 weight 1 maxconn 1000 check inter 2000 rise 2 fall 3
1
2
3
4
systemctl start haproxy
systemctl start keepalived
systemctl enable haproxy
systemctl enable keepalived

2. kubernetes 集群安装

2.1 kk 安装

kk 命令是二进制的,从安装好的机器,直接拷贝到安装集群的 master 机器即可。

1
2
3
4
5
6
7
8
curl -sfL https://get-kk.kubesphere.io | VERSION=v3.1.1 sh -
mv kk /usr/local/sbin/
kk version --show-supported-k8s

# 生成 ssh key
ssh-keygen -t ed25519 -C "master"
# 公钥添加到其它节点
ssh-copy-id -i ~/.ssh/id_ed25519.pub root@其它节点IP

2.2 kk 配置

1
2
3
mkdir k8s
cd k8s
# kk create config --with-kubernetes v1.29.2

直接使用如下配置文件 config-sample.yaml,然后修改下节点信息,如下:

内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
apiVersion: kubekey.kubesphere.io/v1alpha2
kind: Cluster
metadata:
name: sample
spec:
hosts:
- {
name: k8smaster-lv01,
address: 10.0.0.130,
internalAddress: 10.0.0.130,
user: root,
privateKeyPath: "~/.ssh/id_ed25519",
}
- {
name: k8snode-lv01,
address: 10.0.0.131,
internalAddress: 10.0.0.131,
user: root,
privateKeyPath: "~/.ssh/id_ed25519",
}
- {
name: k8snode-lv02,
address: 10.0.0.132,
internalAddress: 10.0.0.132,
user: root,
privateKeyPath: "~/.ssh/id_ed25519",
}
- {
name: k8snode-lv03,
address: 10.0.0.133,
internalAddress: 10.0.0.133,
user: root,
privateKeyPath: "~/.ssh/id_ed25519",
}
roleGroups:
etcd:
- k8smaster-lv01
- k8snode-lv01
- k8snode-lv02
control-plane:
- k8smaster-lv01
worker:
- k8snode-lv01
- k8snode-lv02
- k8snode-lv03
controlPlaneEndpoint:
## Internal loadbalancer for apiservers
# internalLoadbalancer: haproxy

domain: lb.kubesphere.local
address: "10.0.0.140"
port: 8443
kubernetes:
version: v1.29.2
clusterName: cluster.local
autoRenewCerts: true
containerManager: containerd
etcd:
type: kubekey
network:
plugin: calico
kubePodsCIDR: 10.233.64.0/18
kubeServiceCIDR: 10.233.0.0/18
## multus support. https://github.com/k8snetworkplumbingwg/multus-cni
multusCNI:
enabled: false
registry:
privateRegistry: "harbor地址"
namespaceOverride: ""
registryMirrors: [""]
# insecureRegistries: ["非httpsimage地址"] # 如果是 http,请取消注释
#auths:
# "harbor地址":
# username: ''
# password: ''
# skipTLSVerify: true # Allow contacting registries over HTTPS with failed TLS verification.
# plainHTTP: true # Allow contacting registries over HTTP.
addons: []

2.3 kk 安装集群

检查时间,如果时间未同步,重启 chronyd 服务。

1
2
3
4
5
date
systemctl stop chronyd
systemctl start chronyd
chronyc tracking
# chronc -a makestep # 强制同步

集群安装:

1
kk create cluster -f config-sample.yaml -a kubesphere.tar.gz

如果使用的 harbor 是 http 的,kubekey 内部是使用的 https 的仓库,可能会中断安装。我们再次安装,前面二进制文件已经缓存至 kubekey 安装目录,然后不加 -a 参数,就是在线安装,只是使用内部仓库的在线安装方式。

再次集群安装:

1
kk create cluster -f config-sample.yaml

有可能需要处理的地方 1. /etc/containerd/config.toml,里面改成私有仓库地址

1
sandbox_image = "harbor地址/library/kubesphere/pause:3.9"
  1. 如果你需要 coredns 添加 hosts,注意 /etc/resolv.conf 里面要设置 DNS,否则 coredns 可能无法启动。 kubectl edit cm coredns -n kube-system
1
2
3
4
5
6
7
8
9
10
11
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
hosts /etc/coredns/hosts {
IP 域名
fallthrough
}

kubectl edit cm nodelocaldns -n kube-system

1
2
3
4
5
6
7
8
9
10
11
12
13
}
.:53 {
errors
cache 30
reload
loop
bind 169.254.25.10
# forward . /etc/resolv.conf
forward . 10.233.0.3 {
force_tcp
}
prometheus :9253
}

2.4 kk 安装集群失败处理

在 master 节点执行,注意确认正确的节点和危险性!!!

1
kk delete cluster -f config-sample.yaml

3. kubesphere 安装

根据官方文档来即可。

1
2
curl -L -O https://github.com/kubesphere/ks-installer/releases/download/v3.4.1/cluster-configuration.yaml
curl -L -O https://github.com/kubesphere/ks-installer/releases/download/v3.4.1/kubesphere-installer.yaml
1
2
3
4
5
6
spec:
persistence:
storageClass: ""
authentication:
jwtSecret: ""
local_registry: 你的harbor地址/library # 镜像都放在 library 下并保持原来目录结构
1
2
sed -i "s#^\s*image: kubesphere.*/ks-installer:.*#        image: 你的harbor地址/library/kubesphere/ks-installer:v3.4.1#" kubesphere-installer.yaml

安装:

1
2
kubectl apply -f kubesphere-installer.yaml
kubectl apply -f cluster-configuration.yaml

最容易出问题的就是镜像拉取,随时查看并解决镜像问题。

1
2
kubectl get pod -A
kubectl describe pod <pod名> -n <namespace>

4. 暴露 kubesphere 控制台

4.1 安装 metallb

推荐 helm 方式安装,这里略,详情参考 metallb 官方文档。 安装好后配置一个全局 IP 池。

4.2 打开全局网关

使用 http://某一节点IP:30880` 登录 ks 后,在集群设置中,打开全局网关,注意使用Loadbalancer` 类型暴露,因为有全局 IP 池可用,按理说随便选一个负载均衡厂商即可,因为没有 metallb 选项。

4.3 ingress 暴露 ks 控制台

console-ingress.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/upstream-vhost: ks-console.kubesphere-system.svc.cluster.local
name: ks-console
namespace: kubesphere-system
spec:
rules:
- host: ks域名
http:
paths:
- backend:
service:
name: ks-console
port:
number: 80
path: /
pathType: ImplementationSpecific
1
kubectl apply -f console-ingress.yaml

参考资料:
[1] https://www.kubesphere.io/zh/docs/v3.4/installing-on-linux/introduction/air-gapped-installation/
[2] https://www.kubesphere.io/zh/docs/v3.4/installing-on-kubernetes/on-prem-kubernetes/install-ks-on-linux-airgapped/

1. 准备环境

1.1 机器准备

4 台机器,操作系统:Ubuntu 24.04/RHEL8/CentOS9

1
2
3
4
10.111.3.53 master1
10.111.3.54 master2
10.111.3.55 master3
10.111.3.57 node4

1.2 安装依赖和配置

所有节点都需要执行:

Ubuntu:

1
2
3
4
5
6
7
apt-get install -y socat conntrack ebtables ipset chrony # containerd

#echo 'export KKZONE=cn' >> /etc/profile

#cat>/etc/default/kubelet<EOF
#KUBELET_EXTRA_ARGS="--container-runtime-endpoint=unix:///var/run/containerd/containerd.sock"
#EOF

RHEL 系:

1
2
3
4
5
6
7
yum install -y socat conntrack ebtables ipset chrony # containerd

#echo 'export KKZONE=cn' >> /etc/profile

#cat>/etc/sysconfig/kubelet<EOF
#KUBELET_EXTRA_ARGS="--container-runtime-endpoint=unix:///var/run/containerd/containerd.sock"
#EOF

1.3 安装负载均衡 keepalived + haproxy

安装 keepalived haproxy

1
2
apt-get -y install keepalived haproxy
# yum -y install keepalived haproxy

新建用户

1
useradd -r -u 139 -g 100 -s /sbin/nologin keepalived_script

配置 keepalived /etc/keepalived/keepalived.conf (注意配置中注释,不同节点权重不同)内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
! /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
script_user keepalived_script
enable_script_security
router_id LVS_DEVEL
max_auto_priority 99
}
vrrp_script check_apiserver {
script "/etc/keepalived/check_apiserver.sh"
interval 3
weight -2
fall 10
rise 2
}

vrrp_instance VI_1 {
# 只配置一个 MASTER,其它的配置为 BACKUP
state MASTER
# 注意网卡名
interface enp0s18
virtual_router_id 60
# MASTER 权重最高,尽量全部设置为不同的权重
priority 101
authentication {
auth_type PASS
auth_pass k8s
}
virtual_ipaddress {
10.111.3.99
}
track_script {
check_apiserver
}
}

/etc/keepalived/check_apiserver.sh 内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash

# if check error then repeat check for 12 times, else exit
err=0
for k in $(seq 1 12)
do
check_code=$(curl -k https://localhost:6443)
if [[ $check_code == "" ]]; then
err=$(expr $err + 1)
sleep 5
continue
else
err=0
break
fi
done

if [[ $err != "0" ]]; then
# if apiserver is down send SIG=1
echo 'apiserver error!'
exit 1
else
# if apiserver is up send SIG=0
echo 'apiserver normal!'
exit 0
fi

chmod a+x /etc/keepalived/check_apiserver.sh

配置 haproxy /etc/haproxy/haproxy.cfg 内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
global
log 127.0.0.1 local0 err
maxconn 50000
uid 138
gid 138
#daemon
pidfile haproxy.pid

defaults
mode http
log 127.0.0.1 local0 err
maxconn 50000
retries 3
timeout connect 5s
timeout client 30s
timeout server 30s
timeout check 2s

listen admin_stats
mode http
bind 0.0.0.0:1080
log 127.0.0.1 local0 err
stats refresh 30s
stats uri /haproxy-status
stats realm Haproxy\ Statistics
stats auth admin:k8s
stats hide-version
stats admin if TRUE

frontend k8s-https
bind 0.0.0.0:8443
mode tcp
#maxconn 50000
default_backend k8s-https

backend k8s-https
mode tcp
balance roundrobin
server master1 10.111.3.53:6443 weight 1 maxconn 1000 check inter 2000 rise 2 fall 3
server master2 10.111.3.54:6443 weight 1 maxconn 1000 check inter 2000 rise 2 fall 3
server master3 10.111.3.55:6443 weight 1 maxconn 1000 check inter 2000 rise 2 fall 3

1
2
3
4
systemctl start haproxy
systemctl start keepalived
systemctl enable haproxy
systemctl enable keepalived

2. 集群安装

2.1 kk 安装

1
2
3
4
5
6
7
8
curl -sfL https://get-kk.kubesphere.io | VERSION=v3.1.1 sh -
mv kk /usr/local/sbin/
kk version --show-supported-k8s

# 生成 ssh key
ssh-keygen -t ed25519 -C "master"
# 公钥添加到其它节点
ssh-copy-id -i ~/.ssh/id_ed25519.pub root@其它节点IP

2.2 kk 配置

1
2
3
mkdir k8s
cd k8s
kk create config --with-kubernetes v1.29.2

会生成配置文件 config-sample.yaml,然后修改下节点信息,如下:

内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
apiVersion: kubekey.kubesphere.io/v1alpha2
kind: Cluster
metadata:
name: vdi-k8s
spec:
hosts:
- {name: master1, address: 10.111.3.53, internalAddress: 10.111.3.53, user: root, privateKeyPath: "~/.ssh/id_ed25519"}
- {name: master2, address: 10.111.3.54, internalAddress: 10.111.3.54, user: root, privateKeyPath: "~/.ssh/id_ed25519"}
- {name: master3, address: 10.111.3.55, internalAddress: 10.111.3.55, user: root, privateKeyPath: "~/.ssh/id_ed25519"}
- {name: node4, address: 10.111.3.57, internalAddress: 10.111.3.57, user: root, privateKeyPath: "~/.ssh/id_ed25519"}
roleGroups:
etcd:
- master1
- master2
- master3
control-plane:
- master1
- master2
- master3
worker:
- node4
controlPlaneEndpoint:
## Internal loadbalancer for apiservers
# internalLoadbalancer: haproxy

domain: lb.kubesphere.local
address: "10.111.3.99" # 注意修改
port: 8443
kubernetes:
version: v1.29.2
clusterName: cluster.local
autoRenewCerts: true
containerManager: containerd
etcd:
type: kubekey
network:
plugin: calico
kubePodsCIDR: 10.233.64.0/18
kubeServiceCIDR: 10.233.0.0/18
## multus support. https://github.com/k8snetworkplumbingwg/multus-cni
multusCNI:
enabled: false
registry:
privateRegistry: ""
namespaceOverride: ""
registryMirrors: []
insecureRegistries: []
addons: []

2.3 kk 安装集群

检查时间,如果时间未同步,重启 chronyd 服务。

1
2
3
4
5
date
systemctl stop chronyd
systemctl start chronyd
chronyc tracking
# chronc -a makestep # 强制同步

集群安装: kk create cluster -f config-sample.yaml

环境检测通过

后面都是自动的,等待即可。

2.4 kk 安装集群失败处理

在 master 节点执行,注意确认正确的节点和危险性!!!

1
kk delete cluster -f config-sample.yaml

# 1. 目的

vscode 作为当前最多人使用的编辑器和开发工具,其最强大之处就是有成熟的插件社区,但是使用过程中难免就会遇到插件功能不够称心或者插件不维护的情况。

随着 typescript 的越来越普及,很多人都会 ts,那为何不自己开发或者二次开发称心如意的插件呢?而插件的开发就需要测试,手工测试难免遗漏,那就给 vscode 扩展加上单元、集成测试吧。

[Mocha](https://mochajs.org/) 作为 vscode 官方测试工具 [vscode-test](https://github.com/microsoft/vscode-test-cli) 的默认框架,必须编译成 js 后才可以测试,我是不大爽的,奈何 vscode 扩展集成测试必须使用它,但是单元测试可以使用 [jest](https://jestjs.io/) / [vitest](https://vitest.dev/) 直接来测试 ts 代码。

本文使用插件 【[turbo-file-header](https://github.com/ygqygq2/turbo-file-header)】 仓库作为示例来讲解 [vitest 作为 vscode 扩展单元测试](#2-vitest-作为-vscode-扩展单元测试)、[vscode-test 集成测试](#3-vscode-test-集成测试)、[mocha 自定义脚本集成测试](#4-自定义-runner-集成测试)。

# 2. vitest 作为 vscode 扩展单元测试 单元测试使用 `.spec.ts` 作为测试文件和后缀集成测试区分开来。使用 [jest-mock-vscode](https://github.com/streetsidesoftware/jest-mock-vscode) npm 包来整合 vitest,其使用说明我不再作详细说明,我这里列一下我的 `vite.config.ts` 配置:

`vite.config.ts`:

``` /// import path from ‘path’;

import { defineConfig } from ‘vite’;

export default defineConfig({ plugins: [], resolve: { alias: [ { find: /^~(.+)/, replacement: path.join(process.cwd(), ‘node_modules/$1’), }, { find: /^@\/(.+)/, replacement: path.join(process.cwd(), ‘src/$1’), }, ], }, test: { include: [‘src/test/unit/**/*.spec.ts’], coverage: { exclude: [‘node_modules’, ‘out’, ‘src/test’, ‘src/typings’, ‘.vscode-test’], }, }, }); ```

`package.json` scripts 设置:

``` “test:unit”: “vitest unit --watch=false”, “test:coverage”: “vitest run --coverage”, ```

`tsconfig.json` 中的别名设置:

``` “paths”: { “@/*”: [“src/*”] }, ```

# 3. vscode-test 集成测试 集成测试使用 `.test.ts` 作为测试文件后缀和集成测试区分开来。[vscode-test-cli](https://github.com/microsoft/vscode-test-cli/) 配置我不再作详细说明,这里列一下 `.vscode-test.mjs` 配置中的 mocha 别名支持 `require` 配置,当然要安装 `ts-node` `tsconfig-paths` 这两个 npm 包。

``` mocha: { ui: ‘bdd’, require: [‘ts-node/register’, ‘tsconfig-paths/register’], }, ```

断点调试的 `launch.json` 配置:(因为 `.vscode-test.mjs` 有 `launchArgs` 配置,断点配置以 args 为准,为了不冲突,建议单独弄一个配置,我这里使用 `.vscode-test-debug.mjs` )

`launch.json`:

``` { “name”: “Test: e2e”, “type”: “extensionHost”, “request”: “launch”, “runtimeExecutable”: “${execPath}”, “testConfiguration”: “${workspaceFolder}/.vscode-test-debug.mjs”, “args”: [ “${workspaceFolder}/sampleWorkspace/test.code-workspace”, “–extensionDevelopmentPath=${workspaceFolder}”, “–disable-extensions” ], “env”: { “mode”: “debug”, “TS_NODE_PROJECT”: “${workspaceFolder}/tsconfig.json” }, “preLaunchTask”: “npm: test-compile”, “sourceMaps”: true }, ```

`package.json` scripts 设置:(扩展编译是使用的 esbuild 设置的 compile 命令,测试文件使用 tsc 编译)

``` “clean”: “rimraf out/”, “test-compile”: “npm run clean && tsc -p ./ && npm run compile”, “test”: “npm run test-compile && vscode-test”, ```

# 4. 自定义 Runner 集成测试

根据官方说明[设置自定义 Runner](https://code.visualstudio.com/api/working-with-extensions/testing-extension#advanced-setup-your-own-runner) 和我的仓库示例,跟着配置即可。这里列一下我的断点配置:

`launch.json`:

``` { “name”: “Test: e2e use mocha”, “type”: “extensionHost”, “request”: “launch”, “runtimeExecutable”: “${execPath}”, “args”: [ “${workspaceFolder}/sampleWorkspace/test.code-workspace”, “–disable-extensions”, “–extensionDevelopmentPath=${workspaceFolder}”, “–extensionTestsPath=${workspaceFolder}/out/test/suite/index” ], “env”: { “NODE_ENV”: “test”, “TS_NODE_PROJECT”: “${workspaceFolder}/tsconfig.json” }, “outFiles”: [“${workspaceFolder}/out/test/**/*.js”], “preLaunchTask”: “npm: test-compile”, “sourceMaps”: true }, ```

`package.json` scripts 设置:(扩展编译是使用的 esbuild 设置的 compile 命令,测试文件使用 tsc 编译)

``` “clean”: “rimraf out/”, “test-compile”: “npm run clean && tsc -p ./ && npm run compile”, “test:suite:mocha”: “npm run test-compile && node out/test/runTests.js”, ```

# 5. 小结

因为是使用真实实例作集成测试,感觉性能有些低,有些操作要等待点时间才能得到正确结果,所以测试文件中可以考虑适当加个 sleep 函数(timtout 等待)来等待正确结果。

期待以后的版本可以使用 jest 或者 vitest 代替 mocha 集成测试。

参考资料:

[1] https://code.visualstudio.com/api/working-with-extensions/testing-extension [2] https://github.com/microsoft/vscode-test-cli [3] https://github.com/microsoft/vscode-extension-samples/tree/main/helloworld-test-cli-sample [4] https://github.com/microsoft/vscode-extension-samples/tree/main/helloworld-test-sample [5] https://github.com/streetsidesoftware/jest-mock-vscode [6] https://github.com/prettier/prettier-vscode [7] https://github.com/ygqygq2/turbo-file-header

1. 扩展介绍

最近写了一个 vscode 扩展 turbo-file-header,主要是为了实现代码文件中文件头管理,比如增加一些作者信息、lincense 等,欢迎使用。 现在已经实现的功能:

  • 快捷键 ctrl + alt + h 快速插入/更新文件头;
  • 生成项目级自定义文件头模板配置文件;
  • 根据项目级自定义文件头模板配置文件管理文件头;
  • 注释支持语法高亮标签,让注释展示更多信息;
  • 支持新/未知语言配置,在没有相应扩展识别该语言的情况下,使用自定义注释符号;
  • 支持批量插入/更新文件头;
  • 多语言支持;

注意: 为了防止误操作,有版本管理,扩展需要工作目录为 git/svn 项目

2. 扩展使用

通过命令使用:

命令展示

文件头效果

项目级文件头管理:

需要生成自定义模板配置文件 项目级配置文件

  • 自定义模板配置文件生成在 workspace 的 .vscode 目录下;
  • 扩展会优先使用自定义模板配置来生成文件头;
  • 批量插入/更新文件依赖自定义模板配置文件,因为要配置查找哪些文件需要文件头;

1. 问题

kubernetes 升过级,但是 helm release 旧版本中有新版本 api 弃用的 version。
在 helm upgrade 时就出现类似如下版本不匹配的错误,导致 helm upgrade 失败。
当然 helm uninstall 再重新安装可能可以跳过这个问题(只要 charts 不再使用弃用的 api),但是 helm release 的历史记录就丢失了,处理方式显得有点暴力。

1
no matches for kind "HorizontalPodAutoscaler" in version "autoscaling/v2beta1"

2. 解决

通过搜索 helm github issue,发现有个 helm 插件可以解决:
https://github.com/helm/helm-mapkubeapis

插件安装、使用很简单:

1
2
3
helm plugin install https://github.com/helm/helm-mapkubeapis
# 如处理以下 cbf helm release
helm mapkubeapis cbf -n uat

然后就可以愉快地 helm upgrade 了。

插件使用

参考资料:
[1] https://github.com/helm/helm-mapkubeapis

1. 示例功能

示例仓库:https://github.com/ygqygq2/jenkins-jfrog-demo

jenkins 与 jfrog container registry 集成

  • [x] docker maven 打包,普通文件方式上传至 Artifactory,并远程 ssh 执行命令部署
  • [x] docker maven 打包,docker 方式上传至 Artifactory,并远程 ssh docker-compose 部署

2. 环境

  • [x] Docker 20.10.12
  • [x] Docker Compose v2.2.3
  • [x] Jenkins 2.375.3
  • [x] JFrog Container Registry license 7.55.6

3. 小结

示例中已实现日常使用最多的 2 种打包部署方式,直接拿来用或者稍加修改就能用于实际工作中。 虽然 jfrog container registry 免费版本没有 nexus 功能齐全,但是也基本够用了。对于免费 CI/CD 工具也算多了一种选择。

0%