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" """ } } } } } }