Gần đây, tôi được yêu cầu chuyển sang sử dụng Jenkins Pipeline để triển khai dự án, đồng thời có thể thay đổi cấu hình triển khai trực tiếp trên Jenkins. Do các dự án khác sử dụng Pipeline khác biệt nhiều so với dự án của chúng tôi (Jenkins chạy trên Docker, quá trình build không thực hiện trên máy Jenkins mà trên máy phát triển), sau một thời gian thử nghiệm, tôi đã đạt được một phiên bản khả thi.
Kiến trúc tổng quan
- Developer push code lên repository.
- Push code kích hoạt Jenkins job.
- Jenkins thực thi script từ xa trên máy build.
- Máy build pull source, build và đóng gói.
- Chuyển package và Dockerfile tới máy tạo image.
- Tạo Docker image.
- Push image lên registry.
- Triển khai container.
Trong thiết lập này, chức năng build được tích hợp vào máy phát triển, do đó cần cấu hình máy phát triển và máy Jenkins.
1. Chọn registry cho image
Có thể sử dụng registry miễn phí như Docker Hub, Alibaba Cloud, hoặc tự xây dựng registry riêng với Harbor. Ở đây tôi chọn Alibaba Cloud (registry.cn-beijing.aliyuncs.com).
2. Chuẩn bị máy build (builder server)
2.1 Cài đặt Docker
Tham khảo hướng dẫn cài đặt Docker trên CentOS 7.
2.2 Cài đặt Git
sudo yum install -y git
2.3 Cài đặt .NET Core
Tham khảo tài liệu chính thức của Microsoft.
2.4 Tạo SSH key
Sử dụng git qua SSH, cần tạo key pair trên máy build:
sudo ssh-keygen -t rsa
Mặc định lưu tại /root/.ssh/id_rsa. Sao chép nội dung public key (/root/.ssh/id_rsa.pub) vào Git account.
2.5 Clone source code
sudo mkdir -p /opt/src
cd /opt/src
sudo git clone -b dev <git-repo-url>
3. Cấu hình Jenkins server
3.1 Cài đặt Jenkins (chạy trên Docker)
Tham khảo hướng dẫn cài đặt Jenkins.
3.2 Tạo SSH key trên Jenkins server
sudo ssh-keygen -t rsa
cd /root/.ssh
sudo cp id_rsa.pub id_rsa.pub.jenkins
3.3 Thiết lập kết nối SSH không cần mật khẩu từ Jenkins tới máy build
Trên máy build:
sudo mkdir -p /root/.ssh
sudo touch /root/.ssh/authorized_keys
Sao chép file id_rsa.pub.jenkins từ Jenkins vào thư mục /root của máy build, sau đó thêm vào authorized_keys:
cat /root/id_rsa.pub.jenkins >> /root/.ssh/authorized_keys
3.4 Thêm credential trong Jenkins
Vào "Manage Jenkins" → "Manage Credentials" → "Global" → "Add Credentials". Chọn loại "SSH Username with private key", nhập ID, Username, và dán nội dung private key (từ Jenkins server).
3.5 Tạo Pipeline job
Tạo job mới, chọn "Pipeline", và nhập script Jenkinsfile sau:
pipeline {
agent any
stages {
stage('1. Prepare & Upload scripts') {
steps {
script {
sh '''
tee ./gitpull.sh <<-'EOF'
#!/bin/bash
cd /opt/src/abc
sudo git checkout dev
sudo git pull
EOF
tee ./build.sh <<-'EOF'
#!/bin/bash
src="/opt/src/abc"
pub="/opt/publish/abc"
sudo rm -rf "$pub/published"
sudo mkdir -p "$pub/published"
cd "$src"
sudo dotnet publish -o "$pub/published"
EOF
tee ./image_build.sh <<-'EOF'
#!/bin/bash
src="/opt/src/abc"
pub="/opt/publish/abc"
sudo cp "$src/Server/Dockerfile" "$pub"
cd "$pub"
sudo docker build --rm -t registry.cn-beijing.aliyuncs.com/abc/abc:latest .
sudo docker push registry.cn-beijing.aliyuncs.com/abc/abc:latest
EOF
tee ./deploy_stack.sh <<-'EOF'
#!/bin/bash
cd /opt/docker/compose/abc
sudo docker stack rm abc-stack
sudo docker stack deploy -c abc-stack.yml abc-stack
EOF
tee ./abc-stack.yml <<-'EOF'
version: '3.7'
services:
abc:
image: registry.cn-beijing.aliyuncs.com/abc/abc:latest
environment:
- TZ=Asia/Shanghai
- ASPNETCORE_ENVIRONMENT=Production
deploy:
replicas: 1
restart_policy:
condition: any
resources:
limits:
cpus: "2"
memory: 2048M
update_config:
parallelism: 1
delay: 5s
max_failure_ratio: 0.1
order: start-first
ports:
- 35000:5000
networks:
- swarm-net
networks:
swarm-net:
external: true
EOF
'''
}
withCredentials([sshUserPrivateKey(credentialsId: 'mykey', keyFileVariable: 'kf', passphraseVariable: '', usernameVariable: 'usr')]) {
sh "scp -o StrictHostKeyChecking=no -i ${kf} *.sh ${usr}@192.168.1.1:/opt/jenkinsfiles/"
sh "scp -o StrictHostKeyChecking=no -i ${kf} abc-stack.yml ${usr}@192.168.1.1:/opt/docker/compose/abc/"
}
}
}
stage('2. Pull source') {
steps {
withCredentials([sshUserPrivateKey(credentialsId: 'mykey', keyFileVariable: 'kf', passphraseVariable: '', usernameVariable: 'usr')]) {
sh "ssh -o StrictHostKeyChecking=no -i ${kf} ${usr}@192.168.1.1 'cd /opt/jenkinsfiles; sh ./gitpull.sh'"
}
}
}
stage('3. Build source') {
steps {
withCredentials([sshUserPrivateKey(credentialsId: 'mykey', keyFileVariable: 'kf', passphraseVariable: '', usernameVariable: 'usr')]) {
sh "ssh -o StrictHostKeyChecking=no -i ${kf} ${usr}@192.168.1.1 'cd /opt/jenkinsfiles; sh ./build.sh'"
}
}
}
stage('4. Build image') {
steps {
withCredentials([sshUserPrivateKey(credentialsId: 'mykey', keyFileVariable: 'kf', passphraseVariable: '', usernameVariable: 'usr')]) {
sh "ssh -o StrictHostKeyChecking=no -i ${kf} ${usr}@192.168.1.1 'cd /opt/jenkinsfiles; sh ./image_build.sh'"
}
}
}
stage('5. Deploy stack') {
steps {
withCredentials([sshUserPrivateKey(credentialsId: 'mykey', keyFileVariable: 'kf', passphraseVariable: '', usernameVariable: 'usr')]) {
sh "ssh -o StrictHostKeyChecking=no -i ${kf} ${usr}@192.168.1.1 'cd /opt/jenkinsfiles; sh ./deploy_stack.sh'"
}
}
}
}
}
3.6 Chạy job
Vào job vừa tạo, nhấn "Build Now" để thực thi.