Nội dung
File cài đặt trên server
jenkins-setup.sh file
#!/bin/bash apt install openjdk-11-jdk -y java --version wget -p -O - https://pkg.jenkins.io/debian/jenkins.io.key | apt-key add - sh -c 'echo deb http://pkg.jenkins.io/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list' apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 5BA31D57EF5975CA apt-get update apt install jenkins -y systemctl start jenkins ufw allow 8080
docker-setup.sh file
#!/bin/bash sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo \ "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose
docker-compose.yml (cài đặt sonarqube server)
version: "3"
services:
sonarqube:
image: sonarqube:8.9.2-community
depends_on:
- db
environment:
SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
SONAR_JDBC_USERNAME: sonar
SONAR_JDBC_PASSWORD: sonar #tochange
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
- sonarqube_logs:/opt/sonarqube/logs
ports:
- "9000:9000"
db:
image: postgres:12
environment:
POSTGRES_USER: sonar
POSTGRES_PASSWORD: sonar #tochange
volumes:
- postgresql:/var/lib/postgresql
- postgresql_data:/var/lib/postgresql/data
volumes:
sonarqube_data:
sonarqube_extensions:
sonarqube_logs:
postgresql:
postgresql_data:
Cài đặt sonarqube đơn giản hơn với docker run
$ docker run -d --name sonarqube -p 9000:9000 sonarqube
File cấu hình trong dự án
Dockerfile (backend)
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base WORKDIR /app EXPOSE 80 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["BE.csproj", "."] RUN dotnet restore "./BE.csproj" COPY . . WORKDIR "/src/." RUN dotnet build "BE.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "BE.csproj" -c Release -o /app/publish /p:UseAppHost=false FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "BE.dll"]
Dockerfile.dotnet6 (backend)
FROM mcr.microsoft.com/dotnet/sdk:6.0 RUN dotnet tool install --global dotnet-ef ENV PATH="$PATH:/root/.dotnet/tools"
Dockerfile (frontend)
FROM node:16 as build-stage WORKDIR /app COPY package*.json ./ RUN npm install COPY ./ . RUN npm run build:stage FROM nginx as production-stage RUN mkdir /app COPY --from=build-stage /app/dist /app COPY nginx.conf /etc/nginx/nginx.conf
nginx.conf (frontend)
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root /app;
index index.html;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}
docker-compose.yml
version: '3.4'
services:
backend:
container_name: ${NAME_BACKEND}
build:
context: ./BE/
dockerfile: "Dockerfile"
args:
IMAGE_TAG: ${IMAGE_TAG:-1.0.0}
restart: always
image: ${NAME_BACKEND}:${IMAGE_TAG}
ports:
- "8081:80"
environment:
- IMAGE_TAG=${IMAGE_TAG:-1.0.0}
- NAME_BACKEND=${NAME_BACKEND}
frontend:
container_name: ${NAME_FRONTEND}
build:
context: ./FE/
dockerfile: "Dockerfile"
args:
IMAGE_TAG: ${IMAGE_TAG:-1.0.0}
restart: always
image: ${NAME_FRONTEND}:${IMAGE_TAG}
ports:
- "80:80"
environment:
- IMAGE_TAG=${IMAGE_TAG:-1.0.0}
- NAME_FRONTEND=${NAME_FRONTEND}
Jenkinsfile
pipeline {
agent any
environment {
PATH_PROJECT = '/home/projects/devopsedu-central'
SONAR_PROJECT_KEY = 'root_devopsedu-central_AYe5jPZrkpMDT6RJzelC'
SONAR_TOKEN = credentials('token-devopsedu-central')
MIGRATION_NAME = sh (script: 'echo $(date +%Y%m%d%H%M%S)', returnStdout: true).trim()
DOCKER_HUB = 'elroydev'
DOCKERHUB_CREDENTIALS = credentials('dockerhub-credentials')
NAME_BACKEND = 'devopsedu-central-backend'
NAME_FRONTEND = 'devopsedu-central-frontend'
DOCKER_TAG="${GIT_BRANCH.tokenize('/').pop()}-${GIT_COMMIT.substring(0,7)}"
}
stages {
stage('Checkout source') {
steps {
sh "sudo cp -r . $PATH_PROJECT"
}
}
stage('Test backend with dotnet') {
steps {
sh "cd $PATH_PROJECT/BE \
&& docker build -t dotnet6-app -f Dockerfile.dotnet6 . \
&& docker run --rm -v .:/app -w /app dotnet6-app dotnet test"
}
}
stage('Test with sonarqube') {
steps {
withSonarQubeEnv('Sonarqube server connection') {
sh "cd $PATH_PROJECT \
&& docker run --rm -e SONAR_HOST_URL=${env.SONAR_HOST_URL} \
-e SONAR_SCANNER_OPTS='-Dsonar.projectKey=$SONAR_PROJECT_KEY' \
-e SONAR_TOKEN=$SONAR_TOKEN \
-v '.:/usr/src' \
sonarsource/sonar-scanner-cli"
}
}
}
stage('Migration database') {
steps {
script {
try {
timeout(time: 5, unit: 'MINUTES') {
env.userChoice = input message: 'Do you want to migrate the database?',
parameters: [choice(name: 'Versioning Service', choices: 'no\nyes', description: 'Choose "yes" if you want to migrate!')]
}
if (env.userChoice == 'yes') {
echo "Migration success!"
// sh "cd $PATH_PROJECT/BE \
// && docker run --rm -v .:/app -w /app dotnet6-app dotnet ef migrations add $MIGRATION_NAME \
// && docker run --rm -v .:/app -w /app dotnet6-app dotnet ef database update"
} else {
echo "Migration cancelled."
}
} catch (Exception err){
def user = err.getCauses()[0].getUser()
if ('SYSTEM' == user.toString()) {
def didTimeout = true
echo "Timeout. Migration cancelled."
} else {
echo "Migration cancelled by: ${user}"
}
}
}
}
}
stage('Build and push images') {
steps {
script {
env.IMAGE_TAG = DOCKER_TAG
sh "cd $PATH_PROJECT \
&& IMAGE_TAG=${IMAGE_TAG} \
&& NAME_BACKEND=${NAME_BACKEND} \
&& NAME_FRONTEND=${NAME_FRONTEND} \
&& docker-compose build --parallel \
&& docker tag ${NAME_BACKEND}:$DOCKER_TAG ${DOCKER_HUB}/${NAME_BACKEND}:$DOCKER_TAG \
&& docker tag ${NAME_FRONTEND}:$DOCKER_TAG ${DOCKER_HUB}/${NAME_FRONTEND}:$DOCKER_TAG \
&& echo $DOCKERHUB_CREDENTIALS_PSW | docker login -u $DOCKERHUB_CREDENTIALS_USR --password-stdin \
&& docker push ${DOCKER_HUB}/${NAME_BACKEND}:$DOCKER_TAG \
&& docker push ${DOCKER_HUB}/${NAME_FRONTEND}:$DOCKER_TAG \
&& docker rmi ${DOCKER_HUB}/${NAME_BACKEND}:$DOCKER_TAG \
&& docker rmi ${DOCKER_HUB}/${NAME_FRONTEND}:$DOCKER_TAG"
}
}
}
stage('Deploy to staging')
{
steps {
script {
def deploying = "#!/bin/bash\n" +
"docker rm -f ${NAME_BACKEND} ${NAME_FRONTEND}\n" +
"docker pull ${DOCKER_HUB}/${NAME_BACKEND}:$DOCKER_TAG\n" +
"docker pull ${DOCKER_HUB}/${NAME_FRONTEND}:$DOCKER_TAG\n" +
"docker run --name=${NAME_BACKEND} -dp 8081:80 ${DOCKER_HUB}/${NAME_BACKEND}:$DOCKER_TAG\n" +
"docker run --name=${NAME_FRONTEND} -dp 80:80 ${DOCKER_HUB}/${NAME_FRONTEND}:$DOCKER_TAG"
sshagent(credentials: ['jenkins-ssh-key']) {
sh """
ssh -o StrictHostKeyChecking=no -i jenkins-ssh-key [email protected] "echo \\\"${deploying}\\\" > deploy.sh && chmod +x deploy.sh && ./deploy.sh"
"""
}
}
}
}
}
}
