Chemmy's Blog

chengming0916@outlook.com

1. Co., Ltd.

  • 全称:Company Limited
  • 含义:有限责任公司,常见于英国、中国及亚洲地区
  • 特点:”Co.“为Company缩写,”.“表示缩写符号,”,”用于分隔前后词

2. Inc.

  • 全称:Incorporated
  • 含义:股份有限公司,多用于美国、加拿大
  • 示例:Apple Inc.,强调股东责任限于股份投资

3. LLC

  • 全称:Limited Liability Company
  • 含义:有限责任公司(美国特有形式)
  • 特点:兼具合伙制灵活性与股份制有限责任,如Google LLC

4. GmbH

  • 全称:Gesellschaft mit beschränkter Haftung
  • 含义:有限责任公司,德国及德语区专用
  • 示例:Bosch GmbH1

5. AG

  • 全称:Aktiengesellschaft
  • 含义:股份有限公司,德国及瑞士常见
  • 示例:BMW AG

6. S.A.

- 全称:Société Anonyme(法)/Sociedad Anónima(西)
- 含义:股份有限公司,流行于法国、西班牙等拉丁语系国家
- 示例:L’Oréal S.A.1

7. Plc

  • 全称:Public Limited Company
  • 含义:公众有限公司(英国上市企业专用)
  • 示例:HSBC Holdings plc1

8. 株式会社(Kabushiki Kaisha)

  • 缩写:KK
  • 含义:日本股份有限公司
  • 示例:Toyota Motor Corporation KK

地域差异提示

  • 英国”Ltd.”与美国”LLC”虽均表有限责任,但法律结构不同
  • 荷兰用”BV”(私人有限公司),意大利用”S.p.A.”(股份公司)

一、Nmap 简介

Nmap(Network Mapper)是一款功能强大的开源网络扫描和安全审计工具,由 Gordon Lyon(Fyodor)开发。它被誉为网络安全的“瑞士军刀”,广泛应用于网络发现、端口扫描、服务识别和漏洞评估。

核心功能

  • 主机发现:探测网络中的存活设备。
  • 端口扫描:识别目标主机上端口的开放状态。
  • 服务与版本检测:识别端口上运行的服务及其具体版本。
  • 操作系统检测:通过指纹识别目标主机的操作系统。
  • 脚本引擎:利用 Nmap 脚本引擎(NSE)进行高级漏洞扫描、密码爆破等。

二、安装与环境

2.1 安装方法

  • Windows:从 Nmap 官网 下载安装包,默认包含图形化工具 Zenmap
  • **Linux (如 Kali, Ubuntu)**:
    1
    sudo apt update && sudo apt install nmap

2.2 端口状态说明

Nmap 将端口状态分为六类:

状态 含义
open 端口开放,有应用程序正在监听。
closed 端口关闭,但主机可达(收到 RST 包)。
filtered 端口被防火墙或过滤设备屏蔽,无法确定状态。
unfiltered 端口可访问,但无法确定是开放还是关闭。
open|filtered 无法确定端口是开放还是被过滤。
closed|filtered 无法确定端口是关闭还是被过滤。

三、主机发现扫描

3.1 快速扫描单个主机

1
nmap 192.168.1.1

扫描指定 IP,默认进行最常见的 1000 个 TCP 端口扫描。

3.2 扫描整个网段

1
nmap 192.168.1.0/24

扫描 192.168.1.0192.168.1.255 的所有主机。

3.3 排除特定 IP 扫描

1
2
3
4
5
6
# 排除单个 IP
nmap 192.168.1.0/24 --exclude 192.168.1.100
# 排除多个 IP(用逗号分隔)
nmap 192.168.1.0/24 --exclude 192.168.1.100,192.168.1.200
# 从文件读取排除列表
nmap 192.168.1.0/24 --excludefile exclude.txt

3.4 仅扫描存活主机(Ping 扫描)

1
nmap -sn 192.168.1.0/24

-sn 参数跳过端口扫描,仅探测哪些主机在线(使用 ICMP Echo、TCP SYN 到 443端口、ARP 等)。

3.5 ARP 扫描(局域网高效探测)

1
nmap -PR -sn 192.168.1.0/24
  • -PR:启用 ARP 扫描(仅限同一局域网)。
  • -sn:不进行端口扫描。
    此组合在局域网内速度极快且准确。

四、端口扫描技术

4.1 指定端口扫描

1
2
3
4
5
6
7
8
# 扫描单个端口
nmap -p 80 192.168.1.1
# 扫描多个端口(逗号分隔)
nmap -p 80,443,8080 192.168.1.1
# 扫描端口范围
nmap -p 1-1000 192.168.1.1
# 扫描所有端口(1-65535)
nmap -p- 192.168.1.1

4.2 TCP SYN 扫描(半开放扫描)

1
nmap -sS 192.168.1.1

原理:发送 SYN 包,若收到 SYN/ACK 则端口开放,随后发送 RST 终止连接,不建立完整 TCP 三次握手。需要 root 权限

4.3 TCP Connect 扫描(全连接扫描)

1
nmap -sT 192.168.1.1

原理:完成完整的 TCP 三次握手。这是 Nmap 在非 root 用户下的默认扫描方式,速度较慢且易被日志记录。

4.4 UDP 扫描

1
nmap -sU 192.168.1.1

原理:向目标 UDP 端口发送数据包。若无响应,可能为开放或被过滤;若收到 ICMP 端口不可达,则为关闭。UDP 扫描速度很慢。

4.5 综合扫描示例

1
2
3
4
# 扫描常用 TCP 端口并识别服务
nmap -sS -sV 192.168.1.1
# 同时扫描 TCP 和 UDP 的常见端口
nmap -sS -sU -p T:80,443,U:53,161 192.168.1.1

五、服务与系统识别

5.1 服务版本检测

1
nmap -sV 192.168.1.1

-sV 参数会尝试连接开放的端口,并分析响应以确定服务名称和版本号(如 Apache httpd 2.4.41)。

5.2 操作系统检测

1
sudo nmap -O 192.168.1.1

-O 参数通过分析 TCP/IP 协议栈指纹来猜测目标操作系统。需要 root 权限

5.3 综合信息收集

1
sudo nmap -A 192.168.1.1

-A 参数是“激进模式”的缩写,它同时启用:

  • 操作系统检测 (-O)
  • 版本检测 (-sV)
  • 脚本扫描 (--script=default)
  • 路由追踪 (--traceroute)

六、Nmap 脚本引擎(NSE)应用

NSE 是 Nmap 最强大的功能之一,包含数百个脚本,用于漏洞检测、后门检测、密码审计等。

6.1 使用脚本分类

1
2
3
4
5
6
# 使用所有“漏洞”类脚本进行扫描
nmap --script vuln 192.168.1.1
# 使用所有“安全审计”类脚本
nmap --script safe 192.168.1.1
# 使用所有“入侵检测”类脚本
nmap --script intrusive 192.168.1.1

6.2 使用特定脚本

1
2
3
4
# 使用单个脚本(如 HTTP 标题获取)
nmap --script http-headers 192.168.1.1 -p 80
# 使用多个脚本
nmap --script http-headers,http-title 192.168.1.1 -p 80,443

6.3 常见脚本类别与用途

类别 用途 示例脚本
vuln 检查已知漏洞 http-vuln-cve2017-5638
exploit 利用安全漏洞
auth 身份认证绕过/爆破 http-brute, ftp-brute
discovery 网络服务发现 snmp-info, smb-os-discovery
safe 非侵入式检查 http-robots.txt

七、输出与日志管理

7.1 输出格式

Nmap 支持多种输出格式,便于后续分析或集成到其他工具。

参数 格式 描述
-oN <file> 普通文本 人类可读的标准格式。
-oX <file> XML 结构化数据,便于程序解析。
-oG <file> Grepable 单行格式,便于 grepawk 处理。
-oA <basename> 所有格式 同时生成 .nmap, .xml, .gnmap 三个文件。

示例

1
2
3
4
5
6
# 保存为文本文件
nmap -oN scan_result.txt 192.168.1.1
# 保存为 XML 文件
nmap -oX scan_result.xml 192.168.1.1
# 同时保存三种格式(基础名为 scan)
nmap -oA scan 192.168.1.1

7.2 日志分析工具

  • Zenmap:Nmap 官方图形界面,提供结果可视化、拓扑图、比较扫描结果等功能。
  • Ndiff:命令行工具,用于比较两次扫描结果的差异。
    1
    ndiff scan1.xml scan2.xml

八、性能优化与防火墙规避

8.1 调整扫描速度

Nmap 提供从 T0(极慢)到 T5(极快)的时序模板。

1
2
3
4
5
6
# 慢速扫描,减少被检测的风险
nmap -T0 192.168.1.1
# 快速扫描(默认是 T3)
nmap -T4 192.168.1.1
# 极速扫描,可能丢失数据包
nmap -T5 192.168.1.1

8.2 数据包分片与诱饵

1
2
3
4
5
# 使用数据包分片
nmap -f 192.168.1.1
# 使用诱饵 IP(隐藏真实扫描源)
nmap -D RND:10 192.168.1.1 # 生成10个随机诱饵
nmap -D decoy1,decoy2,ME,decoy3 192.168.1.1 # 指定诱饵,ME代表自己

8.3 源端口与 MAC 地址欺骗

1
2
3
4
5
# 指定源端口(如伪装成 DNS 流量)
nmap --source-port 53 192.168.1.1
# 欺骗 MAC 地址
nmap --spoof-mac 0 192.168.1.1 # 随机 MAC
nmap --spoof-mac 00:11:22:33:44:55 192.168.1.1 # 指定 MAC

九、实战综合示例

9.1 完整内网信息收集

1
2
3
# 发现内网存活主机,扫描其前1000个TCP端口,识别服务版本和操作系统
sudo nmap -sn 192.168.1.0/24
sudo nmap -sS -sV -O -p 1-1000 --open 192.168.1.100

9.2 Web 服务器深度扫描

1
2
# 扫描 Web 服务器常见端口,进行漏洞检测
nmap -sS -sV -p 80,443,8080,8443 --script http-vuln*,http-enum 192.168.1.100

9.3 保存并对比扫描结果

1
2
3
4
5
6
# 第一次扫描
nmap -oA baseline 192.168.1.0/24
# 一段时间后第二次扫描
nmap -oA current 192.168.1.0/24
# 使用 ndiff 比较差异
ndiff baseline.xml current.xml

十、法律与道德声明

重要提示

  1. 授权原则仅在您拥有明确授权的网络或设备上使用 Nmap。未经授权扫描他人网络是违法行为,可能构成“计算机欺诈与滥用法案”(CFAA)等罪名。
  2. 教学目的:本文所有示例仅用于网络安全教学、授权下的渗透测试及个人网络管理
  3. 责任自负:使用者需对自身行为承担全部法律和道德责任。

合规使用场景

  • 扫描您自己拥有的服务器和网络。
  • 在明确的书面授权下,为客户进行安全评估。
  • 在隔离的实验室环境(如 Kali Linux 虚拟机网络)中进行学习。

十一、总结与进阶

Nmap 是一个功能极其丰富的工具,本文仅涵盖了其核心功能。要成为专家,建议:

  1. 查阅官方文档https://nmap.org/book/
  2. 学习 NSE 脚本编写:自定义脚本以满足特定需求。
  3. 结合其他工具:将 Nmap 与 Wireshark(流量分析)、Metasploit(漏洞利用框架)等工具结合,构建完整的攻防实验环境。

一、核心定位

本文作为GitOps环境搭建系列的第六篇,聚焦Gitea代码仓库的部署与配置。Gitea是轻量级、高性能的Git服务,专为资源受限环境设计,适合在K3s集群中搭建私有代码仓库。

在GitOps环境中,Gitea扮演”代码托管中枢”角色,作为GitOps流程的单一可信数据源,存储所有应用配置、部署清单和流水线定义。通过Gitea与PostgreSQL、Redis的集成,为后续的Tekton CI流水线和ArgoCD GitOps部署提供可靠的代码存储和版本管理能力。

二、部署前置检查

部署前需验证基础组件(PostgreSQL、Redis)状态及K3s集群环境:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1. 验证K3s集群状态
kubectl get nodes

# 2. 验证PostgreSQL运行状态
kubectl get pods -n postgres
kubectl exec -it postgres-0 -n postgres -- psql -U gitea -d gitea -c "SELECT 1;" 2>/dev/null || echo "需要创建gitea数据库"

# 3. 验证Redis运行状态
kubectl get pods -n redis
kubectl exec -it redis-0 -n redis -- redis-cli -a redis123456 ping

# 4. 验证Traefik运行状态
kubectl get pods -n kube-system -l app=traefik

# 5. 验证cert-manager运行状态
kubectl get pods -n cert-manager

# 6. 验证域名解析
nslookup gitea.example.io

前置条件检查清单:

  • K3s集群运行正常
  • PostgreSQL数据库可用(已创建gitea数据库)
  • Redis缓存服务可用
  • Traefik反向代理运行正常
  • cert-manager证书管理可用
  • 域名gitea.example.io已解析至K3s节点IP

三、创建Gitea命名空间

为保持资源隔离规范,Gitea独立运行在gitea命名空间:

1
2
3
4
5
6
7
8
# gitea-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: gitea
labels:
app: gitea
component: gitops

应用配置:

1
2
3
4
kubectl apply -f gitea-namespace.yaml

# 验证命名空间创建
kubectl get namespace gitea

四、部署Gitea持久化存储

采用local-path存储类,保障Gitea数据持久化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# gitea-pvc-local-path.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gitea-pvc
namespace: gitea
labels:
app: gitea
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 50Gi

五、配置Gitea应用

5.1 创建Gitea配置文件

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
# gitea-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: gitea-config
namespace: gitea
labels:
app: gitea
data:
# 基础配置
APP_NAME: "Gitea Git Repository"
RUN_MODE: prod
HTTP_PORT: "3000"

# 数据库配置(使用前序部署的PostgreSQL)
DB_TYPE: postgres
DB_HOST: postgres.postgres.svc.cluster.local:5432
DB_NAME: gitea
DB_USER: gitea
DB_PASSWD: gitea123

# Redis缓存配置
REDIS_TYPE: redis
REDIS_HOST: redis.redis.svc.cluster.local:6379
REDIS_PASSWORD: redis123456

# 安全配置
DISABLE_REGISTRATION: "true"
REQUIRE_SIGNIN_VIEW: "true"
ENABLE_OPENID_SIGNIN: "false"
ENABLE_OPENID_SIGNUP: "false"

# SSH配置
SSH_LISTEN_PORT: "2222"
SSH_PORT: "22"

# 路径配置
GITEA_APP_INI: /data/gitea/conf/app.ini
GITEA_CUSTOM: /data/gitea
GITEA_WORK_DIR: /data
GITEA_TEMP: /tmp/gitea
TMPDIR: /tmp/gitea
HOME: /data/gitea/git

# 功能开关
ACTIONS_ENABLED: "false"
GITEA_packages_ENABLED: "false"
GITE_actions_ENABLED: "false"

# Webhook配置
GITEA_webhook_ALLOWED_HOST_LIST: "*.example.io"
GITEA_webhook_SKIP_TLS_VERIFY: "true"

5.2 创建Gitea数据库(如未创建)

1
2
3
4
# 在PostgreSQL中创建gitea数据库
kubectl exec -it postgres-0 -n postgres -- psql -U postgres -c "CREATE DATABASE gitea;"
kubectl exec -it postgres-0 -n postgres -- psql -U postgres -c "CREATE USER gitea WITH PASSWORD 'gitea123';"
kubectl exec -it postgres-0 -n postgres -- psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE gitea TO gitea;"

六、部署Gitea服务

6.1 创建Service资源

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
# gitea-service.yaml
apiVersion: v1
kind: Service
metadata:
name: gitea-http
namespace: gitea
labels:
app: gitea
spec:
ports:
- name: http
port: 3000
protocol: TCP
targetPort: 3000
selector:
app: gitea
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: gitea-ssh
namespace: gitea
labels:
app: gitea
spec:
ports:
- name: ssh
port: 22
protocol: TCP
targetPort: 22
selector:
app: gitea
type: ClusterIP

6.2 部署Gitea应用

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
# gitea-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitea
namespace: gitea
labels:
app: gitea
spec:
replicas: 1
selector:
matchLabels:
app: gitea
template:
metadata:
labels:
app: gitea
spec:
containers:
- name: gitea
image: gitea/gitea:1.22.0-rootless
envFrom:
- configMapRef:
name: gitea-config
imagePullPolicy: IfNotPresent
ports:
- containerPort: 22
name: ssh
protocol: TCP
- containerPort: 3000
name: http
protocol: TCP
volumeMounts:
- mountPath: /tmp
name: temp
- mountPath: /data
name: gitea-persistent-storage
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /api/healthz
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /api/healthz
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
restartPolicy: Always
volumes:
- name: temp
emptyDir: {}
- name: gitea-persistent-storage
persistentVolumeClaim:
claimName: gitea-pvc

七、配置HTTPS访问

7.1 创建Gitea证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# gitea-certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: gitea-cert
namespace: gitea
spec:
secretName: gitea-tls-secret
issuerRef:
name: selfsigned-cluster-issuer
kind: ClusterIssuer
commonName: gitea.example.io
dnsNames:
- gitea.example.io
duration: 2160h # 90天
renewBefore: 360h # 过期前15天自动续签
privateKey:
algorithm: RSA
size: 2048
usages:
- server auth

7.2 配置IngressRoute

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# gitea-ingressroute.yaml
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: gitea-websecure
namespace: gitea
annotations:
traefik.ingress.kubernetes.io/service.sticky.cookie: "true"
spec:
entryPoints:
- websecure
routes:
- match: Host(`gitea.example.io`) && PathPrefix(`/`)
kind: Rule
services:
- name: gitea-http
passHostHeader: true
port: 3000
tls:
secretName: gitea-tls-secret

八、应用所有配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 一键部署Gitea
kubectl apply -f gitea-pvc-local-path.yaml
kubectl apply -f gitea-config.yaml
kubectl apply -f gitea-service.yaml
kubectl apply -f gitea-deployment.yaml
kubectl apply -f gitea-certificate.yaml
kubectl apply -f gitea-ingressroute.yaml

# 或使用在线配置(推荐)
kubectl apply -f https://gitee.com/Chemmy/kube-template/raw/master/devops/Gitea/gitea-pvc-local-path.yaml
kubectl apply -f https://gitee.com/Chemmy/kube-template/raw/master/devops/Gitea/gitea-config.yaml
kubectl apply -f https://gitee.com/Chemmy/kube-template/raw/master/devops/Gitea/gitea-service.yaml
kubectl apply -f https://gitee.com/Chemmy/kube-template/raw/master/devops/Gitea/gitea-deployment.yaml
kubectl apply -f https://gitee.com/Chemmy/kube-template/raw/master/devops/Gitea/gitea-certificate.yaml
kubectl apply -f https://gitee.com/Chemmy/kube-template/raw/master/devops/Gitea/gitea-ingressroute.yaml

九、验证部署结果

9.1 验证组件状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看Gitea Pod状态
kubectl get pods -n gitea -w

# 查看PVC状态
kubectl get pvc -n gitea

# 查看Service状态
kubectl get svc -n gitea

# 查看证书状态
kubectl get certificate -n gitea
kubectl describe certificate gitea-cert -n gitea

# 查看IngressRoute状态
kubectl get ingressroute -n gitea

9.2 测试Gitea功能

1
2
3
4
5
6
7
8
9
10
11
12
# 等待Gitea完全启动(约1-2分钟)
sleep 60

# 测试HTTP访问
curl -k https://gitea.example.io

# 查看Gitea日志
kubectl logs -f deployment/gitea -n gitea --tail=50

# 进入容器检查状态
kubectl exec -it $(kubectl get pod -n gitea -l app=gitea -o jsonpath='{.items[0].metadata.name}') -n gitea -- /bin/sh
# 容器内执行:gitea admin status

9.3 初始化Gitea管理员

首次访问Gitea需要完成初始化配置:

  1. 浏览器访问:https://gitea.example.io
  2. 数据库类型:PostgreSQL
  3. 数据库主机:postgres.postgres.svc.cluster.local:5432
  4. 数据库名称:gitea
  5. 数据库用户名:gitea
  6. 数据库密码:gitea123
  7. 站点名称:Gitea Git Repository
  8. 服务器域名:gitea.example.io
  9. 管理员账号设置:
    • 用户名:gitea_admin
    • 密码:GiteaAdmin123!
    • 确认密码:GiteaAdmin123!
    • 邮箱:admin@example.io

十、服务访问方式

10.1 集群内访问

  • Web界面gitea-http.gitea.svc.cluster.local:3000
  • Git HTTPhttp://gitea-http.gitea.svc.cluster.local:3000
  • Git SSHssh://git@gitea-ssh.gitea.svc.cluster.local:22

10.2 集群外访问

  • Web界面https://gitea.example.io
  • Git HTTPhttps://gitea.example.io
  • Git SSHssh://git@gitea.example.io:22(需配置SSH端口转发)

十一、日常运维命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看实时日志
kubectl logs -f deployment/gitea -n gitea

# 重启Gitea服务
kubectl rollout restart deployment/gitea -n gitea

# 备份Gitea数据
kubectl cp gitea/$(kubectl get pod -n gitea -l app=gitea -o jsonpath='{.items[0].metadata.name}'):/data ./gitea-backup-$(date +%Y%m%d)

# 进入容器调试
kubectl exec -it $(kubectl get pod -n gitea -l app=gitea -o jsonpath='{.items[0].metadata.name}') -n gitea -- /bin/sh

# 查看资源使用情况
kubectl top pods -n gitea

十二、常见问题修复

问题现象 排查方向 修复方案
Gitea无法启动 数据库连接失败 检查PostgreSQL连接:kubectl logs deployment/gitea -n gitea
证书签发失败 cert-manager配置 检查证书状态:kubectl describe certificate gitea-cert -n gitea
HTTPS访问失败 IngressRoute配置 检查IngressRoute:kubectl describe ingressroute gitea-websecure -n gitea
SSH连接失败 端口配置/网络 确认SSH Service端口正确,检查网络策略
性能问题 资源不足 调整Deployment资源配置,增加Redis缓存使用
数据持久化失败 PVC绑定问题 检查PVC状态:kubectl describe pvc gitea-pvc -n gitea

十三、配置参考

所有Gitea配置文件和部署脚本请参考:
https://gitee.com/Chemmy/kube-template/tree/master/devops/Gitea

该目录包含:

  • Gitea完整部署配置
  • 数据库初始化脚本
  • 生产环境优化配置
  • 备份和恢复脚本
  • 集成配置示例(与Tekton、ArgoCD集成)

总结

本文完成了Gitea在K3s集群中的标准化部署,实现了代码托管服务的完整功能。Gitea作为GitOps环境的核心组件,为后续的Tekton CI流水线和ArgoCD GitOps部署提供了可靠的代码存储和版本管理基础。

部署完成后,建议创建测试仓库,验证Git操作和Webhook功能正常。下一篇文章将部署ArgoCD,为GitOps环境提供持续部署能力。

在Docker容器中部署运行Python应用时,日志打印是排查故障、监控服务运行状态的核心环节。与本地运行环境不同,Docker容器的文件系统具有临时性,且日志需被容器引擎统一捕获、管理。因此,Python在Docker环境下打印日志的核心原则是:将日志直接输出至标准输出(stdout)和标准错误(stderr),由Docker自动捕获,无需手动写入本地文件。本文汇总了实用实现方案、常见问题解决方案及生产环境最佳实践,帮助开发者高效、规范地实现Docker环境下的Python日志打印。

一、基础日志打印方式:print函数(快速上手)

对于简单测试场景或小型脚本开发,Python内置的print()函数是最便捷的日志打印方式。print()函数默认将内容输出至stdout,而Docker会自动捕获stdout与stderr的输出内容,无需额外配置即可快速查看日志。

示例代码:

1
2
3
print("Python应用启动中...")
print("核心功能执行完成")
print("错误:参数传入异常", file=sys.stderr) # 输出到stderr,区分正常日志与错误日志

Docker端核心日志查看命令:

1
2
3
4
5
# 查看指定容器的全部日志
docker logs 容器名/容器ID

# 实时跟踪日志(类似tail -f,适合监控服务状态)
docker logs -f 容器名/容器ID

注意:print()仅适用于简单场景,其不支持日志级别、时间戳等关键信息,无法满足生产环境下日志分类、追溯、分析的管理需求。

二、生产环境最佳实践:logging模块

对于正式项目或生产环境,推荐使用Python标准库中的logging模块。该模块支持日志级别(DEBUG、INFO、ERROR等)、自定义日志格式、时间戳等实用功能,且能完美适配Docker环境,核心配置要点是将日志强制输出至stdout,确保Docker能及时、准确捕获日志。

2.1 logging模块基础配置(Docker友好版)

配置时需明确指定日志输出至stdout,避免日志被缓冲或输出至其他位置,导致Docker无法捕获,具体示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import logging
import sys

# 配置日志:指定级别、格式、输出目标,适配Docker环境
logging.basicConfig(
level=logging.INFO, # 日志级别,可根据需求调整为DEBUG(调试)/ERROR(错误)
format="%(asctime)s - %(levelname)s - %(message)s", # 日志格式(包含时间戳、日志级别、日志内容)
handlers=[
logging.StreamHandler(sys.stdout) # 强制输出到stdout,确保Docker正常捕获
]
)

# 初始化日志对象
logger = logging.getLogger(__name__)

# 打印不同级别的日志(适配不同场景的日志记录需求)
logger.debug("调试信息:参数初始化完成")
logger.info("服务启动成功,监听端口8000")
logger.warning("警告:配置文件未指定超时时间,建议补充")
logger.error("错误:数据库连接失败,请检查连接配置")

2.2 配置核心说明

  1. logging.StreamHandler(sys.stdout):明确将日志输出至stdout,避免因Python默认输出方式差异,导致Docker日志捕获延迟、丢失或异常;

  2. 日志级别:需根据项目实际需求设置,生产环境建议使用INFO及以上级别,避免调试日志过多占用资源、干扰问题定位;

  3. 日志格式:包含时间戳(asctime)、日志级别(levelname)、日志内容(message),可快速追溯日志产生时间、类型及具体信息,便于后续日志分析和故障定位。

三、关键问题解决:Docker日志不输出/延迟

很多开发者在实践中会遇到“Python代码正常打印日志,但Docker logs无法查看”或“日志延迟输出”的问题,核心原因是Python默认开启输出缓冲,导致日志未及时刷新至stdout,Docker无法捕获。以下提供两种高效解决方案,优先推荐第二种,更适配生产环境。

3.1 运行时添加-u参数(快速临时解决)

在运行Python脚本时,添加-u参数(即unbuffered无缓冲模式),可强制日志实时输出,避免缓冲导致的日志卡住、延迟问题。

直接运行命令:

1
python -u app.py

Dockerfile中CMD指令写法:

1
CMD ["python", "-u", "app.py"]

3.2 设置环境变量(优雅持久解决)

在Dockerfile中添加PYTHONUNBUFFERED环境变量,其效果与-u参数完全一致,无需修改Python脚本运行命令,更适合生产环境的Docker镜像标准化构建。

Dockerfile中添加指令:

1
ENV PYTHONUNBUFFERED=1

该环境变量会直接关闭Python的输出缓冲机制,确保日志实时刷新至stdout,保障Docker能及时捕获每一条日志,避免延迟或丢失。

四、完整Dockerfile示例(可直接复用)

结合上述最佳实践,以下提供完整的Dockerfile示例,包含基础镜像选择、环境变量配置、代码复制及应用运行指令,可直接复用,确保Python日志正常打印、Docker可正常捕获。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 选择轻量Python基础镜像(推荐3.10+版本,兼顾性能与兼容性)
FROM python:3.11-slim

# 关键配置:关闭Python输出缓冲,确保日志实时输出至stdout
ENV PYTHONUNBUFFERED=1

# 设置容器工作目录,规范文件组织结构
WORKDIR /app

# 复制本地Python代码至容器工作目录
COPY . .

# 运行Python应用(无需额外添加-u参数,环境变量已生效)
CMD ["python", "app.py"]

五、Docker日志常用操作命令

日志打印后,需通过Docker命令查看、筛选、监控日志,以下汇总最常用的操作命令,覆盖日常开发、测试及运维场景,简单易上手。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 查看指定容器的全部日志
docker logs 容器名/容器ID

# 2. 实时跟踪日志(实时监控服务运行状态,排查实时故障)
docker logs -f 容器名/容器ID

# 3. 查看最近N行日志(避免日志过多刷屏,快速定位最新问题)
docker logs --tail 100 容器名/容器ID # 示例:查看最近100行日志

# 4. 带时间戳查看日志(精准定位具体时间点的故障日志)
docker logs -t 容器名/容器ID

# 5. 查看指定时间段的日志(需结合-t参数,精准筛选目标日志)
docker logs -t --since="2024-05-01T10:00:00" 容器名/容器ID

六、不推荐做法及避坑提醒

在Docker环境下,以下日志打印方式不推荐使用,易导致日志丢失、无法管理、排查困难等问题,需重点规避:

  1. 不推荐将日志写入容器本地文件:Docker容器的文件系统为临时存储,容器重启后日志会直接丢失;且需额外进入容器查看日志,操作繁琐、效率极低,不利于日志统一管理。

  2. 不推荐不配置logging.handlers:若未指定StreamHandler(sys.stdout),部分Python版本会将日志默认输出至stderr或其他位置,可能导致Docker捕获日志异常、日志错乱。

  3. 不推荐忽略缓冲问题:未关闭Python输出缓冲,会导致日志延迟输出,排查故障时无法及时看到最新日志,增加问题定位难度。

七、总结

Python在Docker环境下打印日志的核心要点的是“标准化输出+无缓冲”,结合实践可总结为4个关键步骤,简单易落地:

  1. 简单测试场景:使用print()函数,直接输出至stdout/stderr,快速实现日志打印;

  2. 生产环境:使用logging模块,配置StreamHandler(sys.stdout),自定义日志格式,满足日志分类、追溯需求;

  3. 避免日志延迟/丢失:通过设置ENV PYTHONUNBUFFERED=1或运行时添加-u参数,关闭Python输出缓冲;

  4. 日志管理:使用Docker日志命令(docker logs)查看、监控日志,无需手动管理日志文件,提升效率。

按照以上最佳实践,可确保Python应用在Docker环境下的日志清晰、实时、可管理,为后续故障排查、服务监控提供有力支撑,提升开发与运维效率。

1. 准备环境

  • 准备开发包:包含头文件(.h)、库文件(.dll或.so)及对接文档
  • 安装依赖:确保Python环境已安装ctypes库或第三方库例如Cython(用于复杂场景)
  • 配置路径:将SDK的库路径添加到环境变量或直接在代码中指定路径(推荐方式,不会因为换电脑导致无法编译,例如sdk/windows/sdk.dll)

2. 封装接口

加载SDK

1
2
3
4
5
6
7
8
9
10
import sys
from ctypes import *
from ctypes import wintypes

# 区分Windows和Linux环境,加载不同SDK
if sys.platform.startwith("win"):
sdk = WinDLL("sdk/windows/sdk.dll")
elif sys.platform.startwith("linux"):
sdk = CDLL("sdk/linux/sdk.so")

定义结构体

1
2
3
4
5
6
7
# 定义结构体,需要与SDK头文件一致
class DEMO
_fields_ = [
("fieldname-1", c_int), # int 类型
("fieldname-2", c_int_p), # int 指针
# 其他字段参考SDK文档
]

![[Python对接C库/IMG-20250804110742707.png]]
定义函数原型,需严格对齐SDK中的数据类型和函数参数顺序

1
2
3
4
sdk.Init.restype = c_bool # 映射返回值,Init为C/C++中的函数名
sdk.Init.argtypes = [ # 映射参数列表
c_int, c_int_p, c_char_p
]

3. 接口调用

函数调用

1
sdk.Init(c_int(0), c_int_p(0), c_char_p(b"this is a test"))

带有回调函数的函数调用
回调函数例如

1
int (*Callback) (int, char*);

Python中定义回调函数类型

1
CallbackType = CFUNCTYPE(c_int, c_int, c_char_p) # 返回类型在前,参数在后

若C函数使用__stdcall(常见于Windows API),需要WINFUNCTYPE替代CFUNCTYPE,若为__cdecl(默认),则使用CFUNCTYPE
Python实现回调函数(参数和返回值需与C定义严格一致)

1
2
3
def py_callback(num, text) -> int:
print(f"Received: {num}, {text.decode("utf-8")}")
return 0 # 返回值需与C定义匹配

处理指针参数

若回调参数包含指针,例如void*,需要使用c_void_p类型,并通过cast解析

1
2
3
def py_callback(data_ptr): 
data = cast(data_ptr, POINTER(c_int)).contents.value
return data

注册回调函数

1
2
3
4
c_callback = CallbackType(py_callback) # 使用定义的回调类型包装Python函数
global_keep_alive = c_callback # 关键! 将回调对象保存为全局变量或类属性,防止被回收
sdk.register_callback.argtypes = [c_int, CallbackType]
sdk.register_callback.restype = None

4. 资源释放

退出时需要调用SDK中的清理函数释放资源

1
sdk.Cleanup()

5. 注意事项

  • 结构体指针和缓冲区需要手动分配/释放,避免内存泄漏
  • 不同版本SDK接口可能有差异,建议统一开发与部署环境
  • 映射Windows中特有的类型例如WORD,DWORDwintypes包中
  • C调用Python回调时,若Python函数抛出异常可能导致程序崩溃。需要在回调内部处理异常。
  • 若C函数在子线程中调用回调,需确保Python的GIL(全局解释锁)已获取
    1
    2
    3
    4
    5
    6
    7
    from ctypes import py_object, pythonapi 
    PyGILState_Ensure = pythonapi.PyGILState_Ensure
    PyGILState_Release = pythonapi.PyGILState_Release
    def thread_safe_callback():
    state = PyGILState_Ensure()
    # 执行Python操作
    PyGILState_Release(state)

使用os.stat()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import (
"fmt"
"os"
)

func main() {
dir := "new"
if _, err := os.Stat(dir); os.IsNotExist(err) {
fmt.Println(dir, "does not exist")
} else {
fmt.Println("The provided directory named", dir, "exists")
}
}

使用os.open()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import (
"fmt"
"os"
)

func main() {
dir := "go"
if _, err := os.Open(dir); os.IsNotExist(err) {
fmt.Println("The directory named", dir, "does not exist")
} else {
fmt.Println("The directory namend", dir, "exists")
}
}

使用mkdir()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import (
"fmt"
"os"
)

func main() {
dir := "new"
if err := os.Mkdir(dir, 0755); os.IsExist(err) {
fmt.Println("The directory named", dir, "exists")
} else {
fmt.Println("The directory named", dir, "does not exist")
}
}

ECB模式,PKCS5填充

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
package sm4

import (
"bytes"
"encoding/hex"
"github.com/tjfoc/gmsm/sm4"
)

func SM4EcbEncrypt(key, plaintext []byte) (string, error) {
block, err := sm4.NewCipher(key)
if err != nil {
return "", err
}

plaintext = PKCS5Padding(plaintext, block.BlockSize())
ciphertext := make([]byte, len(plaintext))

for start := 0; start < len(plaintext); start += block.BlockSize() {
block.Encrypt(ciphertext[start:start+block.BlockSize()], plaintext[start:start+block.BlockSize()])
}

return hex.EncodeToString(ciphertext), nil
}

func SM4EcbDecrypt(key []byte, data string) ([]byte, error) {
plaintext, _ := hex.DecodeString(data)
block, err := sm4.NewCipher(key)
if err != nil {
return nil, err
}

ciphertext := make([]byte, len(plaintext))

for start := 0; start < len(plaintext); start += block.BlockSize() {
block.Decrypt(ciphertext[start:start+block.BlockSize()], plaintext[start:start+block.BlockSize()])
}
ciphertext = PKCS5Unpadding(ciphertext)
return ciphertext, nil
}

func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}

func PKCS5Unpadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}

使用sys模块

1
2
3
4
5
6
7
8
9
10
import sys

if sys.platform.startwith("win"):
print("当前系统是Windows")
elif sys.platform.startwith("linux"):
print("当前系统是Linux")
elif sys.platform.startwith("darwin"):
print("当前系统是MAC OS")
else:
print("当前系统是其他操作系统")

使用platform模块

1
2
3
4
5
6
7
8
9
10
11
import platform

system=platform.system()
if system=="Windows":
print("当前系统是Windows")
elif system=="Linux":
print("当前系统是Linux")
elif system=="Darwin":
print("当前系统是MAC OS")
else:
print("当前系统是其他操作系统")

使用os模块

1
2
3
4
5
6
7
8
9
import os

system = os.name
if system == "nt":
print("当前系统是Windows")
elif system == "posix":
print("当前系统是Linux或Mac OS")
else
print("当前系统是其他操作系统")

临时修改

方法一

1
2
3
4
sudo apt update & sudo apt install net-tools    # 安装 net-tools
sudo ifconfig eth0 down # 停用网卡
sudo ifconfig eth0 hw ether AA:BB:CC:DD:EE:FF # 设置 MAC 地址
sudo ifconfig eth0 up # 启用网卡

方法二

1
2
3
sudo ip link set dev eth0 down                      # 停用网卡 
sudo ip link set dev eth0 address AA:BB:CC:DD:EE:FF # 设置 MAC 地址
sudo ip link set dev eth0 up # 启用网卡

永久修改

注意: 永久修改需要停止NetworkManager服务,此服务可能导致修改不生效

1
2
sudo systemctl stop NetworkManager.service
sudo systemctl disable NetworkManager.service

方法一

编辑`/etc/init.d/rc.local``文件,在此配置文件最后追加临时修改网卡MAC命令

1
2
3
4
# 修改 ech0 网卡的 MAC 地址
sudo ifconfig eth0 down # 网卡名称可使用 ifconfig 或 ip addr 查看
sudo ifconfig eth0 hw ether AA:BB:CC:DD:EE:FF
sudo ifconfig eth0 up

方法二

编辑/etc/network/interfaces文件,在此文件后追加

1
2
3
4
5
6
7
8
auto eth0                         # 网卡自动启动
iface eth0 inet static # 静态 IP
address 192.168.1.2 # IP 地址
netmask 255.255.255.0 # 掩码
gateway 192.168.1.1 # 网关
hwaddress ether AA:BB:CC:DD:EE:FF # MAC 地址
dns-nameservers 223.5.5.5 # DNS 多个用空格隔开
dns-search .com # 限制 .com 的查询走上边设置的DNS服务器

修改完成需要重启网络服务使配置生效

1
2
3
4
sudo systemctl restart networking.service 

# 不同系统服务名可能有不太一样,如果找不到就重启系统
sudo reboot

安装Anaconda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
wget https://repo.anaconda.com/archive/Anaconda3-2024.10-1-Linux-x86_64.sh

sudo chmod +x Anaconda3-2024.10-1-Linux-x86_64.sh # 文件添加执行权限

sudo sh Anaconda3-2024.10-1-Linux-x86_64.sh # 执行安装

# Please, press ENTER to continue -> 回车继续
# 阅读协议,同意按回车(跳过ctrl+c)
# Do you accept the license terms? [yes|no] -> 同意协议输入yes回车
# Anaconda3 will now be installed into this location -> 选择安装路径(本文安装/usr/local/anaconda3),等待安装
# by running conda init? [yes|no] -> 是否添加系统环境,输入yes回车
# 待安装结束

# 刷新当前用户环境(激活环境)
source /usr/local/anaconda3/bin

如果是否添加环境变量选择no的话,安装完成需要手动添加环境变量

1
2
export ANACONDA3_ROOT=/usr/local/anaconda3
export PATH=$ANACONDA3_ROOT/bin:$ANACONDA3_ROOT/condabin:$PATH

如果安装时选择了no但安装完成还想初始化可以执行

1
2
source /usr/local/anaconda3/bin/activate
conda init

![[Anaconda环境配置/IMG-20250804110742705.png]]

安装完成后查看.bashrc 中变更内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!
__conda_setup="$('/usr/local/anaconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"
if [ $? -eq 0 ]; then
eval "$__conda_setup"
else
if [ -f "/usr/local/anaconda3/etc/profile.d/conda.sh" ]; then
. "/usr/local/anaconda3/etc/profile.d/conda.sh"
else
export PATH="/usr/local/anaconda3/bin:$PATH"
fi
fi
unset __conda_setup
# <<< conda initialize <<<

解决打开终端Anaconda自启动问题

1
conda config --set auto_activate_base false

镜像源

北京外国语大学

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
channels:
- defaults
show_channel_urls: true
channel_alias: https://mirrors.bfsu.edu.cn/anaconda
default_channels:
- https://mirrors.bfsu.edu.cn/anaconda/pkgs/main
- https://mirrors.bfsu.edu.cn/anaconda/pkgs/free
- https://mirrors.bfsu.edu.cn/anaconda/pkgs/r
- https://mirrors.bfsu.edu.cn/anaconda/pkgs/pro
- https://mirrors.bfsu.edu.cn/anaconda/pkgs/msys2
custom_channels:
conda-forge: https://mirrors.bfsu.edu.cn/anaconda/cloud
msys2: https://mirrors.bfsu.edu.cn/anaconda/cloud
bioconda: https://mirrors.bfsu.edu.cn/anaconda/cloud
menpo: https://mirrors.bfsu.edu.cn/anaconda/cloud
pytorch: https://mirrors.bfsu.edu.cn/anaconda/cloud
simpleitk: https://mirrors.bfsu.edu.cn/anaconda/cloud

上海交通大学

1
2
3
4
5
6
7
8
9
default_channels:
- https://anaconda.mirrors.sjtug.sjtu.edu.cn/pkgs/r
- https://anaconda.mirrors.sjtug.sjtu.edu.cn/pkgs/main
custom_channels:
conda-forge: https://anaconda.mirrors.sjtug.sjtu.edu.cn/cloud/
pytorch: https://anaconda.mirrors.sjtug.sjtu.edu.cn/cloud/
channels:
- defaults
show_channel_urls: true

Anaconda添加删除虚拟环境

1
2
3
4
5
# 新建虚拟环境 -n 为新建虚拟环境命名
conda create -n python310 python=3.10

# 删除虚拟环境
conda remove -n python310 --all

Anaconda切换虚拟环境

1
2
3
4
5
6
7
8
# 查看所有虚拟环境
conda env list

# 激活虚拟环境
conda activate python310

# 退出虚拟环境
conda deactivate python310
0%