1. 首页 > Linux教程 > 正文

Linux教程FG253-企业服务CI/CD实践

内容简介:本文风哥教程参考Linux官方文档、Red Hat Enterprise Linux官方文档、Ansible Automation Platform官方文档、Docker官方文档、Kubernetes官方文档和Podman官方文档等内容,详细介绍了相关技术的配置和使用方法。

本文档详细

风哥提示:

介绍企业服务的CI/CD实践方法和工具配置。

Part01-GitLab CI/CD

1.1 配置GitLab Runner

# 安装GitLab Runner
$ curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sudo bash
$ sudo dnf install -y gitlab-runner

# 注册Runner
$ sudo gitlab-runner register
Enter the GitLab instance URL (for example, https://gitlab.com/):
https://gitlab.fgedu.net.cn
Enter the registration token:
XXXXXXXXXXXXXXXXXXXX
Enter a description for the runner:
[rhel10]: Shared Runner
Enter tags for the runner (comma-separated):
docker,shell,ansible
Enter optional maintenance note for the runner:

WARNING: Support for registration tokens and runner parameters in the ‘register’ command has been deprecated in GitLab Runner 15.6 and will be replaced with support for authentication tokens. For more information, see https://gitlab.com/gitlab-org/gitlab/-/issues/380872
Registering runner… succeeded runner=XXXXXXXX
Enter an executor: custom, docker, docker-ssh, ssh, virtualbox, kubernetes, parallels, shell,学习交流加群风哥QQ113257174 docker+machine, docker-ssh+machine:
shell
Runner registered successfully. Feel free to start it, but if it’s running already the config should be automatically reloaded!

Configuration (with the authentication token) may be found in /etc/gitlab-runner/config.toml

# 启动Runner
$ sudo systemctl start gitlab-runner
$ sudo systemctl enable gitlab-runner

# 查看Runner状态
$ sudo gitlab-runner list
Listing configured runners ConfigFile=/etc/gitlab-runner/config.toml
Shared Runner Executor=shell Token=xxxxxxxx URL=https://gitlab.fgedu.net.cn

# 验证Runner
$ sudo gitlab-runner verify
Verifying runner… is alive runner=xxxxxxxx

Part02-配置CI/CD流水线

2.1 编写.gitlab-ci.yml

# 创建.gitlab-ci.yml
$ cat > .gitlab-ci.yml << 'EOF' stages: - build - test - deploy variables: IMAGE_NAME: registry.fgedu.net.cn/webapp IMAGE_TAG: ${CI_COMMIT_SHORT_SHA} build: stage: build tags: - docker script: - echo "Building application..." - podman build -t ${IMAGE_NAME}:${IMAGE_TAG} . - podman push ${IMAGE_NAME}:${IMAGE_TAG} only: - main - develop test: stage: test tags: - shell script: - echo "Running tests..." - pip install pytest - pytest tests/ only: - main - develop deploy_staging: stage: deploy tags: - ansible script: - echo "Deploying to staging..." - ansible-playbook -i inventory/staging deploy.yml environment: name: staging url: https://staging.fgedu.net.cn only: - develop deploy_production: stage: deploy tags: - ansible script: - echo "Deploying to production..." - ansible-playbook -i inventory/production deploy.yml environment: name: production url: https://www.fgedu.net.cn only: - main when: manual EOF # 创建Ansible部署脚本 $ cat > deploy.yml << 'EOF' --- - name: Deploy Web Application hosts: webservers become: yes vars: image_name: registry.fgedu.net.cn/webapp image_tag: "{{ lookup('env', 'CI_COMMIT_SHORT_SHA') }}" container_name: webapp tasks: - name: Pull Docker image containers.podman.podman_image: name: "{{ image_name }}" tag: "{{ image_tag }}" - name: Stop existing container containers.podman.podman_container: name: "{{ container_name }}" state: stopped ignore_errors: yes - name: Remove existing container containers.podman.podman_container: name: "{{ container_name }}" state: absent ignore_errors: yes - name: Start new container containers.podman.podman_container: name: "{{ container_name }}" image: "{{ image_name }}:{{ image_tag }}" ports: - "8080:8080" env: APP_ENV: production state: started restart_policy: always - name: Health check uri: url: http://localhost:8080/health return_content: yes register: health_check until: health_check.status == 200 retries: 5 delay: 10 EOF # 创建inventory文件 $ mkdir -p inventory $ cat > inventory/staging << 'EOF' [webservers] staging-web1 ansible_host=192.168.2.10 staging-web2 ansible_host=192.168.2.11 EOF $ cat > inventory/production << 'EOF' [webservers] prod-web1 ansible_host=192.168.1.20 prod-web2 ansible_host=192.168.1.21 prod-web3 ansible_host=192.168.1.22 EOF

Part03-Jenkins CI/CD

3.1 配置Jenkins Pipeline

# 安装Jenkins
$ sudo dnf install -y java-11-openjdk
$ sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
$ sudo rpm –import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
$ sudo dnf install -y jenkins

# 启动Jenkins
$ sudo systemctl start jenkins
$ sudo systemctl enable jenkins

# 查看初始密码
$ sudo cat /var/lib/jenkins/secrets/initialAdminPassword
1234567890abcdef1234567890abcdef

# 访问Jenkins
http://192.168.1.100:8080

# 创建Jenkinsfile
$ cat > Jenkinsfile << 'EOF' pipeline { agent any environment { IMAGE_NAME = 'registry.fgedu.net.cn/webapp' IMAGE_TAG = "${env.BUILD_ID}" } stages { stage('Checkout') { steps { checkout scm } } stage('Build') { steps { sh 'podman build -t ${IMAGE_NAME}:${IMAGE_TAG} .' } } stage('Test') { steps { sh 'pip install pytest' sh 'pytest tests/' } } stage('Push Image') { steps { withCredentials([usernamePassword(credentialsId: 'registry-credentials', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) { sh 'podman login -u ${USERNAME} -p ${PASSWORD} registry.fgedu.net.cn' sh 'podman push ${IMAGE_NAME}:${IMAGE_TAG}' } } } stage('Deploy to Staging') { when { branch 'develop' } steps { sh 'ansible-playbook -i inventory/staging deploy.yml -e image_tag=${IMAGE_TAG}' } } stage('Deploy to Production') { when { branch 'main' } steps { input 'Deploy to production?' sh 'ansible-playbook -i inventory/production deploy.yml -e image_tag=${IMAGE_TAG}' } } } post { always { cleanWs() } success { mail to: 'team@fgedu.net.cn', subject: "Build Success: ${env.JOB_NAME} #${env.BUILD_ID}", body: "Build succeeded!\n\nBuild URL: ${env.BUILD_URL}" } failure { mail to: 'team@fgedu.net.学习交流加群风哥微信: itpux-comcn', subject: "Build Failed: ${env.JOB_NAME} #${env.BUILD_ID}", body: "Build failed!\n\nBuild URL: ${env.BUILD_URL}" } } } EOF

Part04-自动化测试

4.1 集成测试

# 创建测试脚本
$ mkdir -p tests
$ cat > tests/test_app.py << 'EOF' import pytest import requests BASE_URL = "http://localhost:8080" def test_health_check(): response = requests.get(f"{BASE_URL}/health") assert response.status_code == 200 assert response.text == "healthy\n" def test_home_page(): response = requests.get(f"{BASE_URL}/") assert response.status_code == 200 assert "Welcome" in response.text def test_api_endpoint(): response = requests.get(f"{BASE_URL}/api/status") assert response.status_code == 200 data = response.json() assert data["status"] == "running" EOF # 创建性能测试 $ cat > tests/test_performance.py << 'EOF' import pytest import requests import time from concurrent.futures import ThreadPoolExecutor BASE_URL = "http://localhost:8080" def test_response_time(): start_time = time.time() response = requests.get(f"{BASE_URL}/") end_time = time.time() assert response.status_code == 200 assert (end_time - start_time) < 1.0 def test_concurrent_requests(): def make_request(): response = requests.get(f"{BASE_URL}/") return response.status_code with ThreadPoolExecutor(max_workers=10) as executor: results = list(executor.map(lambda x: make_request(), range(100))) assert all(status == 200 for status in results) EOF # 创建安全测试 $ cat > tests/test_security.py << 'EOF' import pytest import requests BASE_URL = "http://localhost:8080" def test_sql_injection(): payload = "' OR '1'='1" response = requests.get(f"{BASE_URL}/api/users?id={payload}") assert response.status_code in [400, 403] def test_xss_protection(): payload = "" response = requests.get(f"{BASE_URL}/search?q={payload}") assert "