DevOps 实验项目阶段 6 笔记:Harbor 私有仓库 + Kubernetes 部署 Spring Boot
DevOps 实验项目阶段 6 笔记:Harbor 私有仓库 + Kubernetes 部署 Spring Boot
阶段目标
完成:
Windows IDEA 开发 Spring Boot
↓
Docker 构建镜像
↓
推送 Harbor
↓
Kubernetes 拉取私有镜像
↓
Spring Boot 运行在 K8S
↓
Pod 连接 MySQL
↓
Service 对外暴露访问
一、Harbor 部署
1. 环境
| 服务器 | IP | 用途 |
|---|---|---|
| infra | 192.168.205.146 | MySQL + Harbor |
2. 安装目录
sudo mkdir -p /opt/harbor
sudo chown -R $USER:$USER /opt/harbor
cd /opt/harbor
3. 下载 Harbor
wget https://github.com/goharbor/harbor/releases/download/v2.11.2/harbor-offline-installer-v2.11.2.tgz
tar zxvf harbor-offline-installer-v2.11.2.tgz
cd harbor
二、Harbor 配置
cp harbor.yml.tmpl harbor.yml
vim harbor.yml
1. 修改 hostname
原:
hostname: reg.mydomain.com
改:
hostname: 192.168.205.146
2. 使用 HTTP(实验环境关闭 HTTPS)
注释掉 HTTPS 部分:
#https:
# port: 443
# certificate:
# private_key:
保留 HTTP:
http:
port: 80
3. 设置管理员密码
harbor_admin_password: Harbor@123
三、安装 Harbor
sudo ./prepare
sudo ./install.sh
查看容器:
docker ps
正常应看到:
harbor-core
harbor-db
harbor-jobservice
harbor-portal
registry
nginx
redis
访问地址:http://192.168.205.146
| 字段 | 值 |
|---|---|
| 用户名 | admin |
| 密码 | Harbor@123 |
四、创建 Harbor 项目
| 配置 | 值 |
|---|---|
| 项目名称 | devops |
| 访问级别 | 私有 |
| 容量 | -1 GiB(不限) |
| 镜像代理 | 关闭 |
最终镜像地址格式:
192.168.205.146/devops/<镜像名>:<标签>
五、配置 Docker 访问 HTTP Harbor
因为 Harbor 使用 HTTP(http://192.168.205.146),而 Docker 默认使用 HTTPS。
需要在 /etc/docker/daemon.json 中配置:Windows端在Docker Desktop中配置
{
"registry-mirrors": ["https://docker.m.daocloud.io"],
"insecure-registries": ["192.168.205.146"]
}
重启 Docker:
sudo systemctl daemon-reload
sudo systemctl restart docker
六、Windows Docker 推送 Spring Boot 镜像
1. Docker Desktop 配置
Docker Engine 增加:
{
"insecure-registries": ["192.168.205.146"]
}
2. 登录 Harbor
docker login 192.168.205.146
| 字段 | 值 |
|---|---|
| Username | admin |
| Password | Harbor@123 |
成功输出:Login Succeeded
3. 查看本地镜像
docker images
例如:
devops-demo v1 xxxxxxxx
4. 修改镜像标签
格式:Harbor地址/项目/镜像:版本
docker tag devops-demo:v1 192.168.205.146/devops/devops-demo:v1
5. 推送到 Harbor
docker push 192.168.205.146/devops/devops-demo:v1
成功输出:
v1: digest: sha256:xxxx
Harbor 页面可见:
| 项目 | 镜像仓库 | Tag |
|---|---|---|
| devops | devops-demo | v1 |
七、Kubernetes 创建 Harbor Secret
在 k8s-master(192.168.205.141)上执行:
kubectl create secret docker-registry harbor-secret \
--docker-server=192.168.205.146 \
--docker-username=admin \
--docker-password=Harbor@123 \
--docker-email=admin@example.com
查看:
kubectl get secret
结果:
harbor-secret kubernetes.io/dockerconfigjson
八、Kubernetes 部署 Spring Boot
目录
mkdir ~/devops-demo-k8s
cd ~/devops-demo-k8s
Deployment 文件
创建 devops-demo-deployment.yml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: devops-demo
spec:
replicas: 2
selector:
matchLabels:
app: devops-demo
template:
metadata:
labels:
app: devops-demo
spec:
imagePullSecrets:
- name: harbor-secret
containers:
- name: devops-demo
image: 192.168.205.146/devops/devops-demo:v1
imagePullPolicy: Always
ports:
- containerPort: 8080
env:
- name: SPRING_DATASOURCE_URL
value: "jdbc:mysql://192.168.205.146:3306/devops_demo?useSSL=false&serverTimezone=Asia/Shanghai"
- name: SPRING_DATASOURCE_USERNAME
value: "root"
- name: SPRING_DATASOURCE_PASSWORD
value: "123456"
部署:
kubectl apply -f devops-demo-deployment.yml
查看:
kubectl get pods -o wide
成功:
devops-demo-xxxx 1/1 Running node1
devops-demo-yyyy 1/1 Running node2
九、解决 ImagePullBackOff
问题
Failed to pull image
https://192.168.205.146/v2/
443 connection refused
原因
Kubernetes 节点默认使用 HTTPS 访问镜像仓库,但 Harbor 使用 HTTP。
解决
在所有 K8S 节点(k8s-master、k8s-node1、k8s-node2)上配置 Docker:
{
"insecure-registries": ["192.168.205.146"]
}
重启 Docker:
sudo systemctl daemon-reload
sudo systemctl restart docker
十、创建 Kubernetes Service
创建 devops-demo-service.yml:
apiVersion: v1
kind: Service
metadata:
name: devops-demo-service
spec:
type: NodePort
selector:
app: devops-demo
ports:
- port: 8080
targetPort: 8080
nodePort: 30881
创建:
kubectl apply -f devops-demo-service.yml
查看:
kubectl get svc
结果:
devops-demo-service NodePort 8080:30881/TCP
访问:
http://192.168.205.142:30881
http://192.168.205.143:30881
十一、解决 MySQL Access Denied
错误
Access denied for user 'root'@'192.168.205.142'
排查
进入 MySQL 容器检查用户权限:
docker exec -it mysql8 mysql -uroot -p
SELECT user, host FROM mysql.user;
发现 root@% 已存在。
根本原因
Spring Boot 配置的密码是 Devops@123,但 MySQL 实际密码是 123456。
修复
修改 Deployment 中的环境变量:
- name: SPRING_DATASOURCE_PASSWORD
value: "123456"
重新部署:
kubectl apply -f devops-demo-deployment.yml
kubectl rollout restart deployment devops-demo
十二、最终验证
查看 Pod
kubectl get pods -o wide
结果:
devops-demo-xxxx Running node1
devops-demo-yyyy Running node2
测试接口
curl http://192.168.205.142:30881/api/users
返回:
[
{
...
}
]
说明完整链路成功。
十三、阶段 6 最终架构
Windows IDEA
│
│ Docker Build
▼
Harbor (192.168.205.146)
│
│ imagePullSecret
▼
Kubernetes
│
├─────────────┐
│ │
node1 node2
│ │
Pod Pod
│
Spring Boot
│
MySQL (192.168.205.146)
十四、阶段 6 完成清单
| 项目 | 状态 |
|---|---|
| Harbor 部署 | ✅ |
| Harbor 项目创建 | ✅ |
| Docker 推送镜像 | ✅ |
| Kubernetes Secret | ✅ |
| Kubernetes 拉取私有镜像 | ✅ |
| Deployment 部署 | ✅ |
| Pod 多副本运行 | ✅ |
| Service 暴露 | ✅ |
| Spring Boot 访问 MySQL | ✅ |
| CRUD 接口验证成功 | ✅ |
十五、Systemd 管理 Harbor 自启(运维拓展)
1. Harbor 是怎么跑起来的?
sudo ./install.sh 的本质:
# install.sh 内部做的事情
sudo ./prepare # 根据 harbor.yml 生成 docker-compose.yml
docker compose up -d # 启动所有容器
Harbor 不是一个 systemd 原生服务,它是一个 Docker Compose 项目,由 8 个容器组成:
harbor-core
harbor-db
harbor-jobservice
harbor-portal
registry
nginx
redis
2. Harbor 挂了如何启动?
场景 1:容器还在,只是停了
cd /opt/harbor/harbor
sudo docker compose start
场景 2:想连配置一起重新拉起
cd /opt/harbor/harbor
sudo docker compose up -d
场景 3:改过 harbor.yml 后重启
cd /opt/harbor/harbor
sudo docker compose down -v # 停并删容器,数据不丢
vim harbor.yml # 改配置
sudo ./prepare # 重新生成配置
sudo docker compose up -d # 重新拉起
场景 4:宿主机重启后 Harbor 没起来
cd /opt/harbor/harbor
sudo ./prepare # 可选,配置没变可跳过
sudo docker compose up -d
3. 为什么 restart: always 不够?
Harbor 的每个容器确实设置了 restart: always,但 Docker 重启后仍然起不来,原因有三:
原因 1:Compose 项目网络丢失
Docker 重启前:
harbor_harbor 网络存在
├── harbor-core
├── harbor-db
└── ...
Docker 重启后:
harbor_harbor 网络被清除
容器尝试重启但网络不存在
→ 全部启动失败
原因 2:容器启动顺序被打乱
Harbor 的 8 个容器有严格依赖顺序:
harbor-db → redis → registry → harbor-core → jobservice/portal/nginx
restart: always 只能让每个容器各自重试,不会遵守 Compose 的依赖顺序。
原因 3:类比理解
restart: always = 每条鱼自己努力游回鱼缸
docker compose up -d = 管家重新把鱼缸布置好,再把鱼放回去
4. 正确方案:Systemd 管理 Harbor
创建 service 文件
sudo vim /etc/systemd/system/harbor.service
内容:
[Unit]
Description=Harbor Container Stack
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/harbor/harbor
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
StandardOutput=journal
[Install]
WantedBy=multi-user.target
启用并测试
sudo systemctl daemon-reload
sudo systemctl enable harbor
sudo systemctl start harbor
sudo systemctl status harbor
启动链路
系统开机
│
▼
systemd 启动 Docker(docker.service)
│
│ After=docker.service → 等 Docker 完全就绪
│ Requires=docker.service → Docker 必须活着
▼
systemd 启动 Harbor(harbor.service)
│
│ ExecStart=/usr/bin/docker compose up -d
▼
Docker Compose 重建网络 + 按顺序启动 8 个容器
5. Systemd 单元文件三个区块
[Unit] 区块——描述和依赖
| 参数 | 作用 | 本例用法 |
|---|---|---|
Description | 给人看的描述 | Harbor Container Stack |
After | 启动顺序 | Docker 先起 |
Requires | 强依赖 | Docker 挂了 Harbor 也得停 |
[Service] 区块——怎么运行
| 参数 | 作用 | 本例用法 |
|---|---|---|
Type=oneshot | 一次性任务,执行完就退出 | docker compose up -d 执行完就退 |
RemainAfterExit=yes | 退出后仍标记为 active | 防止 systemd 误判为 failed |
ExecStart | 启动命令 | docker compose up -d |
ExecStop | 停止命令 | docker compose down |
[Install] 区块——何时自启
| 参数 | 作用 | 本例用法 |
|---|---|---|
WantedBy=multi-user.target | 系统正常启动时自动启用 | 开机自启 |
6. 三种常用 Systemd Unit 类型
| 类型 | 后缀 | 作用 | 你项目里的例子 |
|---|---|---|---|
| Service | .service | 管理进程 | docker.service、cri-docker.service、harbor.service |
| Socket | .socket | 管理端口/Unix Socket | cri-docker.socket |
| Timer | .timer | 定时任务 | 还没用到 |
你项目里的组合
Service + Socket(cri-dockerd):
cri-docker.socket(监听 /run/cri-dockerd.sock)
│ 有连接进来时激活
▼
cri-docker.service(启动 cri-dockerd 进程)
纯 Service(Docker、Harbor):
docker.service(直接管理 dockerd 进程)
harbor.service(执行 docker compose up -d)
7. Harbor 起不来的排障清单
# 看哪个容器挂了
cd /opt/harbor/harbor
sudo docker compose ps
# 看核心组件日志
sudo docker compose logs -f harbor-core
sudo docker compose logs -f nginx
sudo docker compose logs -f harbor-db
# 看宿主机端口是否被占
ss -tlnp | grep -E ':80|:443'
# 看磁盘是否满了
df -h
常见坑:
| 问题 | 原因 | 解决 |
|---|---|---|
| nginx 容器起不来 | 80 端口被占用 | 检查宿主机其他 web 服务 |
| harbor-db 一直重启 | 数据盘满了 | df -h 清理磁盘 |
| 客户端登不上 | insecure-registries 没配 | 检查 /etc/docker/daemon.json |
8. 标准恢复 SOP
Harbor 挂了,记住这套命令就够了:
cd /opt/harbor/harbor
sudo docker compose ps # 看状态
sudo docker compose up -d # 起
如果改过配置:
cd /opt/harbor/harbor
sudo ./prepare
sudo docker compose up -d
如果想一劳永逸解决重启问题:
# 创建 systemd 服务
sudo vim /etc/systemd/system/harbor.service
# 粘贴上面的 service 内容
sudo systemctl daemon-reload
sudo systemctl enable harbor
sudo systemctl start harbor
十六、下一阶段
阶段 7:Jenkins CI/CD
目标:把现在的手动流程:
手动 docker build
手动 push Harbor
手动 kubectl apply
变成自动化流水线:
Gitee Push
↓
Jenkins
↓
Maven Build
↓
Docker Build
↓
Push Harbor
↓
kubectl rollout
↓
Kubernetes 自动发布
更多推荐

所有评论(0)