最近在學習如何使用jenkins結合阿里雲k8s自動化集成和部署springcloud微服務和前端next.js項目,如今記錄下來分享給你們,文章有什麼不妥的地方,但願大佬們批評指正。css
一、購買阿里雲ACK集羣(或者自行搭建)html
我購買的是阿里雲ACK託管版,建立集羣地址前端
注意:建立ACK集羣不收費,收費的是裏面的NAT網關、SLB負載均衡、ECS服務器等vue
二、安裝gitlabjava
這裏我以前寫了一篇阿里雲ECS搭建gitlab,安裝gitlab地址node
三、安裝jenkinsreact
這裏我以前寫了一篇阿里雲ECS搭建jenkins,安裝jenkins地址linux
四、安裝dockernginx
使用官網推薦yum 安裝比較方便,安裝以下:git
sudo yum install -y yum-utils \ device-mapper-persistent-data \ lvm2 sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo sudo yum install docker-ce docker-ce-cli containerd.io
Next.js項目:
服務:react+next.js 服務端口:3000, K8S:Deployment+Server+Ingress Pod名:demo-webapp
Dockerfile
FROM node:12 # 設置工做路徑。全部的路徑都會關聯WORKDIR WORKDIR /usr/src/app # 安裝依賴 COPY package*.json ./ RUN npm install # 拷貝源文件 COPY . . # 構建應用 RUN npm run build # 運行應用 CMD [ "npm", "start" ]
一、服務發現:SpringCloud Eureka
微服務名:demo-eureka-server 微服務端口:8761, K8S:Deployment+Service+Ingress(由於要經過網址查看服務註冊狀況,因此要添加ingress) Pod名:demo-eureka-server
二、服務配置:SpringCloud Config
微服務名:demo-config-server 微服務端口:8888, K8S:Deployment+Service Pod名:demo-config-server
三、服務認證和受權:SpringSecurity + Oauth2
微服務名:demo-auth-service 微服務端口:8901, K8S:Deployment+Service Pod名:demo-auth-service
四、服務網關:SpringCloud Zuul
微服務名:demo-auth-service 微服務端口:5555, K8S:Deployment+Service+Ingress(服務網關是全部服務對外惟一入口,因此要配置ingress) Pod名:demo-auth-service
五、編寫Dockerfile、K8S yaml
爲了方便起見,上面的微服務名和Pod名都設置相同名稱,具體的服務開發過程這裏就暫時忽略,後面有時間再寫篇搭建企業級微服務的文章吧,相關微服務的目錄結構以下所示:
注意:不一樣服務之間Dockerfile和k8s yaml文件大同小異,咱們以Eureka爲例:
Eureka Dockerfile:
FROM openjdk:8-jdk-alpine MAINTAINER "zhangwei"<zhangwei900808@126.com> RUN mkdir -p /usr/local/configsvr ARG JAR_FILE ADD ${JAR_FILE} /usr/local/configsvr/app.jar ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/usr/local/configsvr/app.jar"] EXPOSE 8888
Eureka K8S Yaml:
apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: nginx.ingress.kubernetes.io/configuration-snippet: | rewrite ^/eureka/css/(.*)$ /eureka/eureka/css/$1 redirect; rewrite ^/eureka/js/(.*)$ /eureka/eureka/js/$1 redirect; nginx.ingress.kubernetes.io/force-ssl-redirect: 'true' nginx.ingress.kubernetes.io/rewrite-target: /$2 nginx.ingress.kubernetes.io/service-weight: '' generation: 4 name: <podname>-ingress namespace: default spec: rules: - host: baidu.com http: paths: - backend: serviceName: <podname>-svc servicePort: 8761 path: /eureka(/|$)(.*) pathType: ImplementationSpecific --- apiVersion: v1 kind: Service metadata: name: <podname>-svc namespace: default spec: externalTrafficPolicy: Local ports: - nodePort: 31061 port: 8761 protocol: TCP targetPort: 8761 selector: app: <podname> sessionAffinity: None type: NodePort --- apiVersion: apps/v1 kind: Deployment metadata: annotations: deployment.kubernetes.io/revision: '1' generation: 1 labels: app: <podname> name: <podname> namespace: default spec: progressDeadlineSeconds: 600 replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: app: <podname> strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 25% type: RollingUpdate template: metadata: labels: app: <podname> spec: containers: - env: - name: LANG value: C.UTF-8 - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk image: <imagename> imagePullPolicy: IfNotPresent name: <podname> ports: - containerPort: 8761 protocol: TCP resources: requests: cpu: 250m memory: 512Mi terminationMessagePath: /dev/termination-log terminationMessagePolicy: File dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler securityContext: {} terminationGracePeriodSeconds: 30
注意:不一樣的策服務bootstrap.yml和application.yml文件仍是有所區別,好比下面這些:
demo-auth-service、demo-zuul-server 配置bootstrap.yml文件添加SpringCloud Config地址:
spring: application: name: authservice cloud: config: enabled: true # config的Server名 uri: http://demo-config-server-svc:8888
demo-auth-service、demo-zuul-server、demo-config-server 配置application.yml文件添加Eureka地址:
eureka: instance: preferIpAddress: true hostname: demo-eureka-server-svc client: register-with-eureka: true fetch-registry: true service-url: # eureka的Server名 defaultZone: http://demo-eureka-server-svc:8761/eureka/
按照Git工做流,通常咱們能夠分爲:開發環境(develop)、測試環境(release)、預生產環境(uat)和生產環境(prop),對應的分支分別是:dev、test、release、master,所以不一樣階段提交的代碼所屬分支也不相同,且dockerfile、jenkins pipline、k8s yaml文件也不相同,這個要注意下。
jenkins能夠按照git工做流添加:測試視圖、預生產視圖、生產視圖,以下所示:
咱們先建立流水線任務並編寫pipline script腳原本建立CI/CD流水線步驟
先編寫環境變量
environment { GIT_REPOSITORY="前端代碼倉庫地址" K8S_YAML="k8s yaml文件所在目錄" DOCKER_USERNAME="docker 倉庫用戶名" DOCKER_PWD="docker倉庫密碼" ALIYUN_DOCKER_HOST = '阿里雲docker倉庫域名' ALIYUN_DOCKER_NAMESPACE="阿里雲docker倉庫命名空間" ALIYUN_DOCKER_REPOSITORY_NAME="阿里雲docker倉庫命名空間下的倉庫名" }
步驟一:克隆代碼
stage("Clone") { steps { echo "1.Clone Stage" // 刪除文件夾 deleteDir() // 測試分支,jenkins-gitlab-ssh-hash是ssh密鑰,替換成本身的就好 git branch: 'test', credentialsId: 'jenkins-gitlab-ssh-hash', url: "${GIT_REPOSITORY}" script { // 獲取git提交的hash值作爲docker鏡像tag GIT_TAG = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim() // 組裝成完整地址 DOCKER_REPOSITORY = "${ALIYUN_DOCKER_HOST}/${ALIYUN_DOCKER_NAMESPACE}/${ALIYUN_DOCKER_REPOSITORY_NAME}" DOCKER_REPOSITORY_TAG = "${DOCKER_REPOSITORY}:${GIT_TAG}" } } }
步驟二:代碼測試
stage("Test") { steps { echo "2.Test Stage" } }
步驟三:製做docker鏡像
stage("Build") { steps { echo "3.Build Docker Image Stage" sh "docker build -t ${DOCKER_REPOSITORY_TAG} -f docker/Dockerfile ." } }
步驟四:推送docker鏡像
stage("Push") { steps { echo "4.Push Docker Image Stage" //推送Docker鏡像,username 跟 password 爲 阿里雲容器鏡像服務的帳號密碼 sh "docker login --username=${DOCKER_USERNAME} --password=${DOCKER_PWD} ${ALIYUN_DOCKER_HOST}" // 開始推送鏡像到阿里雲docker鏡像倉庫 sh "docker push ${DOCKER_REPOSITORY_TAG}" // 刪除jenkins生成的image sh ''' docker images | grep seaurl | awk '{print $3}' | xargs docker rmi -f ''' } }
步驟五:k8s部署docker鏡像
stage("Deploy") { steps { echo "5.發佈鏡像" // 使用sed替換k8s yaml文件中的<imagename>和<podname> sh "sed -i 's#<imagename>#${DOCKER_REPOSITORY_TAG}#g;s#<podname>#${POD_NAME}#g' ${K8S_YAML}" // 執行應用k8s yaml sh "kubectl apply -f ${K8S_YAML}" } }
sed 語法你們能夠自行百度,這裏我使用的是#分隔,而沒有使用/分隔,緣由是分隔的字符中包含/,因此不能再用。
首先,咱們來看看前端k8s yaml文件
apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: cert-manager.io/cluster-issuer: letsencrypt-prod-http01 kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/force-ssl-redirect: 'true' nginx.ingress.kubernetes.io/service-weight: '' generation: 3 name: <podname>-ingress namespace: default spec: rules: - host: baidu.com http: paths: - backend: serviceName: <podname>-svc servicePort: 3000 path: / pathType: ImplementationSpecific tls: - hosts: - baidu.com secretName: <podname>-ingress --- apiVersion: v1 kind: Service metadata: name: <podname>-svc namespace: default spec: ports: - port: 3000 protocol: TCP targetPort: 3000 selector: app: <podname> sessionAffinity: None type: ClusterIP --- apiVersion: apps/v1 kind: Deployment metadata: annotations: deployment.kubernetes.io/revision: '1' generation: 1 labels: app: <podname> name: <podname> namespace: default spec: progressDeadlineSeconds: 600 replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: app: <podname> strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 25% type: RollingUpdate template: metadata: labels: app:<podname> spec: containers: - image: <imagename> imagePullPolicy: IfNotPresent name: <podname> resources: requests: cpu: 250m memory: 512Mi
上面的yaml文件中包含:deployment、service和ingress,ingress中配置了tls,所以可使用https來訪問域名,而且配置了nginx.ingress.kubernetes.io/force-ssl-redirect: 'true'
因此能夠自動將http 跳轉到https,其中裏面的<podname>和<imagename>是要被sed替換成真實名稱和地址。
完整的pipline script腳本:
pipeline { agent any environment { GIT_REPOSITORY="前端代碼倉庫地址" K8S_YAML="k8s yaml文件所在目錄" DOCKER_USERNAME="docker 倉庫用戶名" DOCKER_PWD="docker倉庫密碼" ALIYUN_DOCKER_HOST = '阿里雲docker倉庫域名' ALIYUN_DOCKER_NAMESPACE="阿里雲docker倉庫命名空間" ALIYUN_DOCKER_REPOSITORY_NAME="阿里雲docker倉庫命名空間下的倉庫名" } stages { stage("Clone") { steps { echo "1.Clone Stage" // 刪除文件夾 deleteDir() // 測試分支,jenkins-gitlab-ssh-hash是ssh密鑰,替換成本身的就好 git branch: 'test', credentialsId: 'jenkins-gitlab-ssh-hash', url: "${GIT_REPOSITORY}" script { // 獲取git提交的hash值作爲docker鏡像tag GIT_TAG = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim() // 組裝成完整地址 DOCKER_REPOSITORY = "${ALIYUN_DOCKER_HOST}/${ALIYUN_DOCKER_NAMESPACE}/${ALIYUN_DOCKER_REPOSITORY_NAME}" DOCKER_REPOSITORY_TAG = "${DOCKER_REPOSITORY}:${GIT_TAG}" } } } stage("Test") { steps { echo "2.Test Stage" } } stage("Build") { steps { echo "3.Build Docker Image Stage" sh "docker build -t ${DOCKER_REPOSITORY_TAG} -f docker/Dockerfile ." } } stage("Push") { steps { echo "4.Push Docker Image Stage" //推送Docker鏡像,username 跟 password 爲 阿里雲容器鏡像服務的帳號密碼 sh "docker login --username=${DOCKER_USERNAME} --password=${DOCKER_PWD} ${ALIYUN_DOCKER_HOST}" // 開始推送鏡像到阿里雲docker鏡像倉庫 sh "docker push ${DOCKER_REPOSITORY_TAG}" // 刪除jenkins生成的image sh ''' docker images | grep seaurl | awk '{print $3}' | xargs docker rmi -f ''' } } stage("Deploy") { steps { echo "5.發佈鏡像" // 使用sed替換k8s yaml文件中的<imagename>和<podname> sh "sed -i 's#<imagename>#${DOCKER_REPOSITORY_TAG}#g;s#<podname>#${POD_NAME}#g' ${K8S_YAML}" // 執行應用k8s yaml sh "kubectl apply -f ${K8S_YAML}" } } } }
點擊當即構建,以下所示:
先編寫環境變量
environment { GIT_REPOSITORY="代碼倉庫地址" MODULE_NAME="maven 模塊名" POD_NAME="k8s pod name" K8S_YAML="${MODULE_NAME}/src/main/k8s/eurekasvr.yaml" DOCKER_USERNAME="docker 倉庫用戶名" DOCKER_PWD="docker倉庫密碼" ALIYUN_DOCKER_HOST = 阿里雲docker倉庫域名' ALIYUN_DOCKER_NAMESPACE="阿里雲docker倉庫命名空間" ALIYUN_DOCKER_REPOSITORY_NAME="阿里雲docker倉庫命名空間下的倉庫名" }
步驟一:克隆代碼
stage("Clone") { steps { echo "1.Clone Stage" // 刪除文件夾 deleteDir() // 測試分支,jenkins-gitlab-ssh-hash是ssh密鑰,替換成本身的就好 git branch: 'test', credentialsId: 'jenkins-gitlab-ssh-hash', url: "${GIT_REPOSITORY}" script { // 獲取git提交的hash值作爲docker鏡像tag GIT_TAG = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim() // 組裝成完整地址 DOCKER_REPOSITORY = "${ALIYUN_DOCKER_HOST}/${ALIYUN_DOCKER_NAMESPACE}/${ALIYUN_DOCKER_REPOSITORY_NAME}" DOCKER_REPOSITORY_TAG = "${DOCKER_REPOSITORY}:${GIT_TAG}" } } }
步驟二:代碼測試
stage("Test") { steps { echo "2.Test Stage" } }
步驟三:製做docker鏡像
stage("Build") { steps { echo "3.Build Server" sh "mvn -e -U -pl ${MODULE_NAME} -am clean package -Dmaven.test.skip=true dockerfile:build -Ddockerfile.tag=${GIT_TAG} -Ddockerfile.repository=${DOCKER_REPOSITORY}" } }
這裏說明一下,由於咱們使用的是maven 多模塊因此打包編譯的時候要分模塊來打包,因此要使用-pl指定模塊名,而後咱們pom.xml中使用了dockerfile-maven-plugin,以下所示:
<build> <plugins> <plugin> <groupId>com.spotify</groupId> <artifactId>dockerfile-maven-plugin</artifactId> <version>1.4.10</version> <configuration> <!-- 指定dockerfile所在目錄--> <dockerfile>src/main/docker/Dockerfile</dockerfile> <buildArgs> <!--提供參數向Dockerfile傳遞--> <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE> </buildArgs> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
步驟四:推送docker鏡像
stage("Push") { steps { echo "4.Push Docker Image Stage" //推送Docker鏡像,username 跟 password 爲 阿里雲容器鏡像服務的帳號密碼 sh "docker login --username=${DOCKER_USERNAME} --password=${DOCKER_PWD} ${ALIYUN_DOCKER_HOST}" // 開始推送鏡像到阿里雲docker鏡像倉庫 sh "docker push ${DOCKER_REPOSITORY_TAG}" // 刪除jenkins生成的image sh ''' docker images | grep seaurl | awk '{print $3}' | xargs docker rmi -f ''' } }
步驟五:k8s部署docker鏡像
stage("Deploy") { steps { echo "5.發佈鏡像" // 使用sed替換k8s yaml文件中的<imagename>和<podname> sh "sed -i 's#<imagename>#${DOCKER_REPOSITORY_TAG}#g;s#<podname>#${POD_NAME}#g' ${K8S_YAML}" // 執行應用k8s yaml sh "kubectl apply -f ${K8S_YAML}" } }
sed 語法你們能夠自行百度,這裏我使用的是#分隔,而沒有使用/分隔,緣由是分隔的字符中包含/,因此不能再用。
首先,咱們來看看Eureka k8s yaml文件
apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: nginx.ingress.kubernetes.io/configuration-snippet: | rewrite ^/eureka/css/(.*)$ /eureka/eureka/css/$1 redirect; rewrite ^/eureka/js/(.*)$ /eureka/eureka/js/$1 redirect; nginx.ingress.kubernetes.io/force-ssl-redirect: 'true' nginx.ingress.kubernetes.io/rewrite-target: /$2 nginx.ingress.kubernetes.io/service-weight: '' generation: 4 name: <podname>-ingress namespace: default spec: rules: - host: baidu.com http: paths: - backend: serviceName: <podname>-svc servicePort: 8761 path: /eureka(/|$)(.*) pathType: ImplementationSpecific --- apiVersion: v1 kind: Service metadata: name: <podname>-svc namespace: default spec: externalTrafficPolicy: Local ports: - nodePort: 31061 port: 8761 protocol: TCP targetPort: 8761 selector: app: <podname> sessionAffinity: None type: NodePort --- apiVersion: apps/v1 kind: Deployment metadata: annotations: deployment.kubernetes.io/revision: '1' generation: 1 labels: app: <podname> name: <podname> namespace: default spec: progressDeadlineSeconds: 600 replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: app: <podname> strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 25% type: RollingUpdate template: metadata: labels: app: <podname> spec: containers: - env: - name: LANG value: C.UTF-8 - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk image: <imagename> imagePullPolicy: IfNotPresent name: <podname> ports: - containerPort: 8761 protocol: TCP resources: requests: cpu: 250m memory: 512Mi terminationMessagePath: /dev/termination-log terminationMessagePolicy: File dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler securityContext: {} terminationGracePeriodSeconds: 30
上面的yaml文件中包含:deployment、service和ingress,ingress中配置了tls,所以可使用https來訪問域名,而且配置了nginx.ingress.kubernetes.io/force-ssl-redirect: 'true'
因此能夠自動將http 跳轉到https,其中裏面的<podname>和<imagename>是要被sed替換成真實名稱和地址。
完整的pipline script腳本:
pipeline { agent any environment { GIT_REPOSITORY="代碼倉庫地址" MODULE_NAME="maven 模塊名" POD_NAME="k8s pod name" K8S_YAML="${MODULE_NAME}/src/main/k8s/eurekasvr.yaml" DOCKER_USERNAME="docker 倉庫用戶名" DOCKER_PWD="docker倉庫密碼" ALIYUN_DOCKER_HOST = 阿里雲docker倉庫域名' ALIYUN_DOCKER_NAMESPACE="阿里雲docker倉庫命名空間" ALIYUN_DOCKER_REPOSITORY_NAME="阿里雲docker倉庫命名空間下的倉庫名" } stages { stage("Clone") { steps { echo "1.Clone Stage" // 刪除文件夾 deleteDir() git branch: 'test',credentialsId: '1297dda3-e592-4e70-8fb0-087a26c08db0', url: "${GIT_REPOSITORY}" script { // 獲取git代碼tag爲docker倉庫tag // GIT_TAG = sh(returnStdout: true,script: 'git describe --tags --always').trim() // 獲取git提交hash作爲docker倉庫tag GIT_TAG = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim() DOCKER_REPOSITORY = "${ALIYUN_DOCKER_HOST}/${ALIYUN_DOCKER_NAMESPACE}/${ALIYUN_DOCKER_REPOSITORY_NAME}" // docker 阿里鏡像倉庫 DOCKER_REPOSITORY_TAG = "${DOCKER_REPOSITORY}:${GIT_TAG}" } } } stage("Test") { steps { echo "2.Test Stage" } } stage("Build") { steps { echo "3.Build Server" sh "mvn -e -U -pl ${MODULE_NAME} -am clean package -Dmaven.test.skip=true dockerfile:build -Ddockerfile.tag=${GIT_TAG} -Ddockerfile.repository=${DOCKER_REPOSITORY}" } } stage("Push") { steps { echo "4.Push Docker Image Stage" //推送Docker鏡像,username 跟 password 爲 阿里雲容器鏡像服務的帳號密碼 sh "docker login --username=${DOCKER_USERNAME} --password=${DOCKER_PWD} ${ALIYUN_DOCKER_HOST}" // 開始推送鏡像到阿里雲docker鏡像倉庫 sh "docker push ${DOCKER_REPOSITORY_TAG}" // 刪除jenkins生成的image sh ''' docker images | grep seaurl | awk '{print $3}' | xargs docker rmi -f ''' } } stage("Deploy") { steps { echo "5.發佈鏡像" sh "sed -i 's#<dockerrepository>#${DOCKER_REPOSITORY_TAG}#g;s#<podname>#${POD_NAME}#g' ${K8S_YAML}" sh "kubectl apply -f ${K8S_YAML}" } } } }
點擊當即構建,以下所示:
能夠經過命令
kubectl get deploy kubectl get pod kubectl get svc kubectl get ingress
還能夠查看pod日誌,對其成功或失敗進行分析:
kubectl logs podname
能夠經過postman接口訪問對外的zuul地址,查看是否能夠認證經過:
能夠經過next.js的部署地址,查看是否部署成功:
一、jenkins pipline script腳本和Dockerfile寫法網上各式各樣,你們找到一個符合標準的就好
二、kubectl apply -f 的做用是:若是沒有建立deployment就會建立,不然就更新
三、微服務k8s yaml文件中Service要設置type: NodePort不然,微服務間不能通訊
四、jenkins 編譯微服務的maven模塊要用到-pl 和 dockerfile-maven-plugin
五、微服務若是要用k8s ingress要設置https,以及http自動跳轉到https
JenkinsPipeline部署一個Kubernetes 應用
採用jenkins pipeline實現自動構建並部署至k8s
Configuring-CI-CD-on-Kubernetes-with-Jenkins
spring-k8s
jenkins pipeline 自動構建並部署至k8s
微服務實戰(一)基於OAUTH2.0統一認證受權的微服務基礎架構
使用cert-manager申請免費的HTTPS證書
SPRINGBOOT利用SPRING.PROFILES.ACTIVE=@SPRING.ACTIVE@不一樣環境下靈活切換配置文件
https://kuboard.cn/learning/k8s-practice/ocp/eureka-server.html#%E6%9F%A5%E7%9C%8B%E9%83%A8%E7%BD%B2%E7%BB%93%E6%9E%9C
kubernetes部署微服務spring cloud的簡單例子
k8s-nginx-ingress eureka二級路徑轉發的問題