- 博客/
使用 helm 在 Kubernetes 中部署 Nexus 私服
Table of Contents
环境说明#
- helm version:
v3.3.1
- kubernetes:
v1.17.9
- nexus:
3.29.0
部署#
准备 storageClass#
非
必要
操作,可以选择手动创建 pvc & pv,如果想部署nfsStorageClass
的话,请参考早期整理的 文档。此篇文档实战部署步骤中基于nfsStorageClass
进行实现。如果是用于生产环境
的话,还是建议使用 ssd 或 iops 较高的磁盘
作为数据盘使用,数据盘管理方式可以使用 localPv or hostPath 绑定到对应节点进行运行。
使用 helm
进行安装#
添加 helm 仓库#
helm repo add stable https //charts.helm.sh/stable
helm repo update
设置部署 values.yaml
配置文件#
可以执行下面命令进行查看
默认
模板部署文件,或点击此 链接 查看。helm show values stable/sonatype-nexus
修改后,完整 values.yaml 文件展示
cat > prod-values.yaml << EOF
statefulset: # 使用 statefulset 进行部署
enabled: true
nexus:
imageName: sonatype/nexus3
imageTag: 3.30.1
imagePullPolicy: IfNotPresent
env:
- name: install4jAddVmParams
value: "-Xms1200M -Xmx1200M -XX:MaxDirectMemorySize=2G -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"
- name: NEXUS_SECURITY_RANDOMPASSWORD
value: "false"
- name: TZ
value: "Asia/Shanghai"
resources:
limits:
cpu: 4
memory: 8192Mi
requests:
cpu: 250m
memory: 4800Mi
persistence:
enabled: true
storageClass: "nfs-retain"
storageSize: "100Gi"
service:
name: nexus-service
enabled: true
serviceType: ClusterIP
ports:
- name: manage # 此处为 service 对应暴露的端口, 如需暴露其他端口时,向下进行添加即可。
targetPort: 8081
port: 8081
- name: docker-proxy
targetPort: 8082
port: 8082
- name: docker-dev
targetPort: 8083
port: 8083
- name: docker-qa
targetPort: 8084
port: 8084
- name: docker-prod
targetPort: 8085
port: 8085
- name: docker-custom
targetPort: 8086
port: 8086
nexusProxy:
enabled: false
EOF
创建部署应用#
kubectl create ns nexus
helm upgrade --install nexus -f ./prod-values.yaml -n nexus stable/sonatype-nexus # 部署安装
使用 traefik 将其暴露#
traefik 部署安装使用,请参考 此篇文档
cat nexus-ingressroute.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: nexus-mirror
namespace: nexus
spec:
entryPoints:
- web
routes:
- match: Host(`mirror.treesir.pub`) && PathPrefix(`/`)
kind: Rule
services:
- name: nexus-service
port: 8081
kubectl apply -f ./nexus-ingressroute.yaml
初始化U/P: admin: admin123
Docker 私服的配置与优化#
此步骤文档,记录创建
custom
、dev
、qa
、prod
环境的 docker 私服,并配置常见 docker proxy 类型私服,进行中间镜像的缓存进行使用。
仓库规划#
docker 私服端口 使用规划#
私服名称 | 私服作用 | 私服类型 | 私服端口 |
---|---|---|---|
docker-custom | 存放,自定义 push的 镜像,与项目环境无关 | hostd | 8086 |
idocker.io | 代理仓库 & custom 仓库的集合 | group | 8082 |
docker-dev | 存放与项目dev环境镜像 | hostd | 8083 |
docker-qa | 存放与项目qa环境镜像 | hostd | 8084 |
docker-prod | 存放与项目prod环境镜像 | hostd | 8085 |
docker 代理仓库列表#
名称 | 私服类型 | 说明 | 地址 |
---|---|---|---|
docker-google | proxy | google 公开镜像 (需扶墙) | https://gcr.io |
docker-k8s | proxy | kubernetes 的官方 google 镜像源(需扶墙) | https://k8s.gcr.io |
docker-aliyun | proxy | aliyun 同步 docker 官方源 (存在部分镜像未同步问题) | https://7bezldxe.mirror.aliyuncs.com |
docker-official | proxy | dockerhub 官方镜像地址(限制带宽,触发条件:匿名用户100 次,认证用户200 次) | https://registry-1.docker.io |
私服创建#
创建 hostd 类型私服#
且示例创建
docker-custom
, 其他仓库创建步骤是一样的,且需更具实际使用情况 更改Deployment policy
策略 & 是否开启匿名拉取镜像
即可,如在prod
环境下,设置策略为Disable redeploy
,即不允许对已存在的镜像做覆盖操作,push 时将提示错误。
创建 docker 使用的 blob 存储
建议对不同仓库类型的 私服做一下,blob存储的隔离 (可选)
选择创建
hostd
类型 docker 私服
创建 proxy 类型代理私服#
且示例创建
docker-official
私服,因官方对匿名用户做了拉取到100次后,将降低带宽的使用,我们可以在配置的过程中,为其添加用户认证来增加拉取限制。
选择创建
proxy
类型 docker 私服
创建 group 类型私服#
创建完成 对应 的 proxy 类型的私服后,就可以进行 group 类型私服, 一组仓库的集合的创建了。
组集合列表,如下所示
创建 group 组
idocker.io
配置 neuxs3 开启 使用 docker的认证管理#
默认情况下,docker 认证在 neuxs3 中是没有被打开的,这里需要配置开启一下才行,不然会导致 执行
docker login
不成功。
使用 ingress 对端口进行暴露#
在使用 traefik 暴露的docker私服中,因默认开启了 443 端口的监听,此时 docker 私服将默认使用
websecure
入口上的 ingressroute 路由规则,如不添加对应证书的话,将导致match
上的规则不被匹配
创建 tls 证书生成脚本#
gen-cert.sh
证书签发脚本,如下所示:
#!/bin/bash
# scripts from https://www.jianshu.com/p/52fedb82ef53
usage()
{
echo "Usage: $0 [-a [rsa|ecc]] [-d <domain>] [-n <name>] [-h]"
echo " Options:"
echo " -a algorithm.[rsa|ecc]"
echo " -d domain.example: xxx.com,abc.org,*.abc.org"
echo " -n server key name"
echo " -h help"
exit 1
}
srv_key_name="server"
while getopts "a:d:n:h" arg #选项后面的冒号表示该选项需要参数
do
case $arg in
a)
alg=$OPTARG #算法
;;
d)
all_domain=$OPTARG #域名,逗号分隔
;;
n)
srv_key_name=$OPTARG #服务器证书名称
;;
h)
usage
exit 0
;;
?) #当有不认识的选项的时候arg为?
usage
exit 1
;;
esac
done
domain="domain.com"
san="DNS:*.${domain},DNS:${domain}"
if [ -n "${all_domain}" ]; then
#分割域名
OLD_IFS="$IFS"
IFS=","
domain_array=($all_domain)
IFS="$OLD_IFS"
domain_len=${#domain_array[@]}
domain=${domain_array[0]}
san=""
for ((i=0;i<domain_len;i++))
{
if [ $i = 0 ];then
san="DNS:${domain_array[i]}"
else
san="${san},DNS:${domain_array[i]}"
fi
}
fi
ca_subj="/C=CN/ST=Hubei/L=Wuhan/O=MY/CN=MY CA"
server_subj="/C=CN/ST=Hubei/L=Wuhan/O=MY/CN=${domain}"
#其中C是Country,ST是state,L是local,O是Organization,OU是Organization Unit,CN是common name
days=14610 # 有效期40年
echo "san:${san}"
sdir="certs"
ca_key_file="${sdir}/ca.key"
ca_crt_file="${sdir}/ca.crt"
srv_key_file="${sdir}/${srv_key_name}.key"
srv_csr_file="${sdir}/${srv_key_name}.csr"
srv_crt_file="${sdir}/${srv_key_name}.crt"
srv_p12_file="${sdir}/${srv_key_name}.p12"
srv_fullchain_file="${sdir}/${srv_key_name}-fullchain.crt"
cfg_san_file="${sdir}/san.cnf"
#algorithm config
if [[ ${alg} = "rsa" ]] ; then
rsa_len=2048
elif [[ ${alg} = "ecc" ]] ; then
ecc_name=prime256v1
else
usage
exit 1
fi #ifend
echo "algorithm:${alg}"
mkdir -p ${sdir}
if [ ! -f "${ca_key_file}" ]; then
echo "------------- gen ca key-----------------------"
if [[ ${alg} = "rsa" ]] ; then
openssl genrsa -out ${ca_key_file} ${rsa_len}
elif [[ ${alg} = "ecc" ]] ; then
openssl ecparam -out ${ca_key_file} -name ${ecc_name} -genkey
fi #ifend
openssl req -new -x509 -days ${days} -key ${ca_key_file} -out ${ca_crt_file} -subj "${ca_subj}"
fi
if [ ! -f "${srv_key_file}" ]; then
echo "------------- gen server key-----------------------"
if [[ ${alg} = "rsa" ]] ; then
openssl genrsa -out ${srv_key_file} ${rsa_len}
elif [[ ${alg} = "ecc" ]] ; then
openssl ecparam -genkey -name ${ecc_name} -out ${srv_key_file}
fi #ifend
openssl req -new -sha256 -key ${srv_key_file} -out ${srv_csr_file} -subj "${server_subj}"
printf "[ SAN ]\nauthorityKeyIdentifier=keyid,issuer\nbasicConstraints=CA:FALSE\nkeyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment\nsubjectAltName=${san}" > ${cfg_san_file}
openssl x509 -req -days ${days} -sha256 -CA ${ca_crt_file} -CAkey ${ca_key_file} -CAcreateserial -in ${srv_csr_file} -out ${srv_crt_file} -extfile ${cfg_san_file} -extensions SAN
cat ${srv_crt_file} ${ca_crt_file} > ${srv_fullchain_file}
openssl pkcs12 -export -inkey ${srv_key_file} -in ${srv_crt_file} -CAfile ${ca_crt_file} -chain -out ${srv_p12_file}
fi
脚本使用用法
./gen-cert.sh -a 算法 -d 域名 -n 证书文件名
执行脚本生成 tls 证书文件#
由于 traefik 在使用 secret 资源对象引用证书文件时,名称必须是
tls.crt
和tls.key
,这里我们在创建的时候就将名称设置为tls
chmod a+x gen-cert.sh
./gen-cert.sh -a ecc -d idocker.io,*.idocker.io -n tls
ls certs/ # 在对应目录下,有如下文件即可
ca.crt ca.key ca.srl san.cnf tls.crt tls.csr tls-fullchain.crt tls.key tls.p12
将生成的证书,使用 secret 资源对象进行存储#
kubectl create secret tls idocker-tls --cert=certs/tls.crt --key=certs/tls.key -n nexus
kubectl get secret idocker-tls -n nexus -o yaml
更新 traefik ingress 资源对象#
最终,完整
nexus-ingressroute.yaml
配置文件如下所示:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: nexus-mirror
namespace: nexus
spec:
entryPoints:
- web
routes:
- match: Host(`mirror.treesir.pub`) && PathPrefix(`/`)
kind: Rule
services:
- name: nexus-service
port: 8081
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: nexus-idocker-http
namespace: nexus
spec:
entryPoints:
- websecure
routes:
- match: Host(`idocker.io`) || (Host(`idocker.io`) && Method(`GET`))
kind: Rule
services:
- name: nexus-service
port: 8082
- match: (Host(`idocker.io`) && Path(`/v1/search`)) || (Host(`idocker.io`) && Method(`PUT`,`HEAD`,`POST`,`PATCH`))
kind: Rule
priority: 100 # 权重值提高,在执行 curl https://idocker.io/v1/search?q=nginx 时,确认路由至 dokcer-custom 私服中
services:
- name: nexus-service
port: 8086
- match: Host(`dev.idocker.io`) && PathPrefix(`/`)
kind: Rule
services:
- name: nexus-service
port: 8083
- match: Host(`qa.idocker.io`) && PathPrefix(`/`)
kind: Rule
services:
- name: nexus-service
port: 8084
- match: Host(`prod.idocker.io`) && PathPrefix(`/`)
kind: Rule
services:
- name: nexus-service
port: 8085
tls:
secretName: idocker-tls
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: redirect-https
namespace: nexus
spec:
redirectScheme:
scheme: https
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: nexus-idocker-https
namespace: nexus
spec:
entryPoints:
- web
routes:
- match: HostRegexp(`idocker.io`, `{subdomain:[a-z]+}.idocker.io`)
kind: Rule
services:
- name: nexus-service
port: 8082
middlewares:
- name: redirect-https
私服的使用#
ingressroute 创建完成后,就是测试对应私服的使用了,直接拿来使用还不行,会提示证书报错,解决方法列表如下:
将对应 CA 证书文件,复制到
/etc/docker/certs.d/idocker.io
目录即可,没有此目录则选择创建它mkdir -p /etc/docker/certs.d/idocker.io scp node1:/data/helm/nexus/certs/ca.crt /etc/docker/certs.d/idocker.io/ mkdir -p /etc/docker/certs.d/qa.idocker.io cp /etc/docker/certs.d/idocker.io/ca.crt /etc/docker/certs.d/qa.idocker.io/
由于,不支持对泛域名的一次认证,如多个子域名时,还需进行一个多次的 copy 操作。
在对应 docker 配置中添加
--insecure-registry
参数,来忽略证书的校验,常见做法是:将配置项更改添加至
/etc/docker/daemon.json
配置文件中cat /etc/docker/daemon.json { "insecure-registries": [ "idocker.io", "dev.idocker.io", "qa.idocker.io", "prod.idocker.io" ] }
或者是在
/usr/lib/systemd/system/docker.service
启动文件中的, ExecStart 配置项中添加启动参数ExecStart=/usr/bin/dockerd --insecure-registry idocker.io,dev.idocker.io...
参考文档#
nexus 其他高级使用说明,请参考 此篇文档
https://doc.traefik.io/traefik/routing/routers/
https://www.jianshu.com/p/52fedb82ef53