前面我給大家分享了關於分布式鏈路追蹤的基本原理和SkyWalking的k8s部署玩法,如果還沒來得及看的朋友可以看我上篇文章。
今天要給大家分享是我們日常工作中最常見的一種場景,那就是部署在k8s環境下的Java微服務,要接入SkyWalking的具體玩法,通過這個過程咱們可以更深入的理解SkyWalking進行數據采集的邏輯,也能更深刻地從運維角度理解日常工作中所寫的Java微服務被無侵入的方式接入分布式鏈路追蹤系統的過程!
廢話不多說,接下來就讓我們開啟干貨模式吧!
Java微服務接入SkyWalking的方式
在上篇文章關於SkyWalking基本原理的內容中有講過,SkyWalking的數據采集主要是通過業務探針(Agent)來實現的,針對不同的編程語言SkyWalking提供了對應的Agent實現。Java微服務接入SkyWalking可以使用“SkyWalking Java Agent”來上報監控數據。
這就需要Java微服務在部署啟動的過程中需要獲取"SkyWalking Java Agent"探針包,並在啟動參數中通過“--javaagent:xxx”進行參數指定。而具體的集成方式大致有以下三種:
-
使用官方提供的基礎鏡像;
-
將agent包構建到已存在的基礎鏡像中;
-
通過sidecar 模式掛載agent;
其中前兩種方式主要是通過在構建Docker鏡像的過程中將Agent依賴打包集成到Java服務的Docker鏡像中,而sidecar模式則是利用k8s的相關特性來實現在容器啟動時掛載Agent相關依賴。
如果微服務是直接部署在Kubernetes集群,那么采用sidecar模式來使用SkyWalking Agent會更加方便,因為這種方式不需要修改原來的基礎鏡像,也不需要重新構建新的服務鏡像,而是會以sidecar模式,通過共享的volume將agent所需的相關文件直接掛載到已經存在的服務鏡像中。
構建SkyWalking Agent鏡像
在開始以sidecar方式,將一個用Spring Cloud框架編寫的Java微服務接入SkyWalking之前,我們需要構建SkyWalking Java Agent的公共鏡像,具體步驟如下:
1)、下載SkyWalking官方發行包,並解壓到指定目錄
#下載skywalking-8.3.0 for es7版本的發布包,與部署的skywalking后端版本一致
$ wget https://mirror.bit.edu.cn/apache/skywalking/8.3.0/apache-skywalking-apm-es7-8.3.0.tar.gz
#將下載的發布包解壓到當前目錄
$ tar -zxvf apache-skywalking-apm-es7-8.3.0.tar.gz
2)、構建skywalking-agentsidecar鏡像並push至hub私有鏡像倉庫
在前面步驟中解壓的skywalking發行包的同級目錄編寫Dockerfile文件,具體內容如下:
FROM busybox:latest
ENV LANG=C.UTF-8
RUN set -eux && mkdir -p /usr/skywalking/agent
add apache-skywalking-apm-bin-es7/agent /usr/skywalking/agent
WORKDIR
在上述Dockefile文件中使用是的bosybox鏡像,而不是SkyWalking的發行鏡像,這樣可以確保構建出來的sidecar鏡像保持最小。
完成Docker文件編寫后,執行鏡像構建命令:
#執行鏡像構建命令
$ docker build . -t springcloud-action/skywalking-agent-sidecar:8.3.0
Sending build context to Docker daemon 556.5MB
Step 1/5 : FROM busybox:latest
latest: Pulling from library/busybox
d60bca25ef07: Pull complete
Digest: sha256:49dae530fd5fee674a6b0d3da89a380fc93746095e7eca0f1b70188a95fd5d71
Status: Downloaded newer image for busybox:latest
---> a77dce18d0ec
Step 2/5 : ENV LANG=C.UTF-8
---> Running in e95b4c25ebf3
Removing intermediate container e95b4c25ebf3
---> 83f22bccb6f3
Step 3/5 : RUN set -eux && mkdir -p /usr/skywalking/agent
---> Running in 49c2eac2b6ab
+ mkdir -p /usr/skywalking/agent
Removing intermediate container 49c2eac2b6ab
---> 89cf3ce8238e
Step 4/5 : add apache-skywalking-apm-bin/agent /usr/skywalking/agent
---> 91fe5f06948f
Step 5/5 : WORKDIR /
---> Running in 6a64553f1870
Removing intermediate container 6a64553f1870
---> 7e73ddba48bb
Successfully built 7e73ddba48bb
Successfully tagged springcloud-action/skywalking-agent-sidecar:8.3.0
為了驗證構建的鏡像是否成功,可以通過命令查看本地構建的鏡像,命令如下:
#查看本地鏡像信息
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
springcloud-action/skywalking-agent-sidecar 8.3.0 7e73ddba48bb 2 minutes ago 32.2MB
...
3)、將打包的鏡像推送到harbor鏡像倉庫
為了便於后續微服務直接使用已經構建好的SkyWalking Agent SideCar鏡像,我們可以將其push至私有Harbor鏡像倉庫中。具體命令如下:
#登錄鏡像倉庫,輸入用戶名密碼(admin/Harbor12345)
$ docker login http://10.211.55.2:8080
Username: admin
Password:
Login Succeeded
這里的Harbor私有鏡像倉庫一般公司都會自己搭建,接下來我們對構建的鏡像打tag並上傳,具體如下:
#這里將原先構建的鏡像安裝{鏡像倉庫地址}/項目名稱/鏡像名稱的方式打tag
$ docker tag springcloud-action/skywalking-agent-sidecar:8.3.0 10.211.55.2:8080/springcloud-action/skywalking-agent-sidecar
之后可以具體查看已經打過tag鏡像信息,命令如下:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
springcloud-action/skywalking-agent-sidecar 8.3.0 e21040c57e42 2 weeks ago 32.2MB
10.211.55.2:8080/springcloud-action/skywalking-agent-sidecar latest e21040c57e42 2 weeks ago 32.2MB
...
接下來我們將打過tag的鏡像推送至私有Harbor倉庫,具體操作如下:
#將鏡像推送到Harbor私有鏡像倉庫
$ docker push 10.211.55.2:8080/springcloud-action/skywalking-agent-sidecar
The push refers to repository [10.211.55.2:8080/springcloud-action/skywalking-agent-sidecar]
e80d641c3ed9: Layer already exists
11fe582bd430: Layer already exists
1dad141bdb55: Layer already exists
latest: digest: sha256:b495c18c3ae35f563ad4db91c3db66f245e6038be0ced635d16d0e3d3f3bcb80 size: 946
完成后可以進入harbor倉庫進行查看,如下圖所示:
SideCar模式接入SkyWalking服務
上面我們通過手工構建的方式構建了SkyWalking Java Agent的公共Docker鏡像,並將其Push到了我們的私有Harbor鏡像倉庫,接下來我們將演示如何通過編寫Kubernetes服務發布文件,來將Java服務發布到K8s集群的過程中自動以SideCar的形式集成Agent、並接入SkyWalking服務。
而這個過程才是作為一個Java程序員最關心的步驟。在開始下面步驟前,你應該通過IDEA構建一個Spring Boot微服務工程,具體構建的過程就不掩飾了,但重點是你這個Spring Boot工程應該支持構建Docker鏡像,以Maven為例,需要在pom.xml文件中添加打包插件,具體如下:
<!--添加將Java應用打包為Docker Image的Maven插件-->
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.13</version>
<executions>
<execution>
<id>build-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<!--指定Dockerfile文件位置-->
<dockerfile>docker/Dockerfile</dockerfile>
<repository>${docker.repository}/springcloud-action/${app.name}</repository>
<!--<tag>${project.version}</tag>-->
<buildArgs>
<!--提供參數向Dockerfile傳遞-->
<JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
其中
FROM openjdk:8u191-jre-alpine3.9
ENTRYPOINT ["/usr/bin/java", "-jar", "/app.jar"]
ARG JAR_FILE
ADD ${JAR_FILE} /app.jar
EXPOSE 8080
這就是一個簡單的鏡像構建文件,如果不采用sidecar方式,那么就需要在服務鏡像構建文件中添加SkyWalking Agent的相關集成,但這里我們是sidecar方式,所以服務鏡像構建文件就不用那么復雜了!
此外
<properties>
<!--定義Docker鏡像倉庫地址-->
<docker.repository>10.211.55.2:8080</docker.repository>
<!--定義項目名稱作為鏡像名稱生成的組成部分-->
<app.name>chapter10-monitor-demo</app.name>
</properties>
接下來具體講述接入步驟:
1)、將Java微服務工程打包成Docker鏡像並Push到Harbor鏡像倉庫
# Maven項目構建,會自動根據pom.xml中的相關插件配置進行docker鏡像構建
$ mvn clean install -X
查看本地新構建的鏡像信息,具體如下:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
10.211.55.2:8080/springcloud-action/chapter10-monitor-demo latest 3ae132cdfeb7 12 seconds ago 121MB
10.211.55.2:8080/springcloud-action/skywalking-agent-sidecar latest e21040c57e42 2 weeks ago 32.2MB
springcloud-action/skywalking-agent-sidecar 8.3.0 e21040c57e42 2 weeks ago 32.2MB
...
將微服務鏡像push到Harbor鏡像倉庫:
$ docker push 10.211.55.2:8080/springcloud-action/chapter10-monitor-demo
The push refers to repository [10.211.55.2:8080/springcloud-action/chapter10-monitor-demo]
5f3427edfc10: Pushed
925523484e00: Layer already exists
344fb4b275b7: Layer already exists
bcf2f368fe23: Layer already exists
latest: digest: sha256:b424180c56b28a9a7704a1f6476f4247fad12cc27721c21fce32149a8f344dee size: 1159
3)、微服務Kubernetes發布文件集成SkyWalking Agent實現埋點
到這里你並沒有發現為了將Java服務接入SkyWalking,你需要在Java微服務本身做任何動作,而接下來在k8s部署文件中的將演示,為什么要將這種方式稱之為SideCar。
其主要原理是通過Kubernetes的初始化容器initContainers來實現的,initContainers是一種專用容器,可以在應用容器啟動之前運行,可以用於完成應用啟動前的必要初始化工作。具體的Kubernetes部署文件(deploy-skywalking.yml)內容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: chapter10-monitor-demo
spec:
selector:
matchLabels:
app: chapter10-monitor-demo
replicas: 1
#設置滾動升級策略
#Kubernetes在等待設置的時間后才開始進行升級,例如5秒
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
#升級過程中最多可以比原先設置多出的Pod數量
maxSurge: 1
#升級過程中Deployment控制器最多可以刪除多少個舊Pod,主要用於提供緩沖時間
maxUnavailable: 1
template:
metadata:
labels:
app: chapter10-monitor-demo
spec:
#構建初始化鏡像(通過初始化鏡像的方式集成SkyWalking Agent)
initContainers:
- image: 10.211.55.2:8080/springcloud-action/skywalking-agent-sidecar:latest
name: sw-agent-sidecar
imagePullPolicy: IfNotPresent
command: ["sh"]
args:
[
"-c",
"mkdir -p /skywalking/agent && cp -r /usr/skywalking/agent/* /skywalking/agent",
]
volumeMounts:
- mountPath: /skywalking/agent
name: sw-agent
containers:
- name: chapter10-devops-demo
image: 10.211.55.2:8080/springcloud-action/chapter10-monitor-demo:latest
env:
#這里通過JAVA_TOOL_OPTIONS,而不是JAVA_OPTS可以實現不通過將agent命令加入到java應用jvm參數而實現agent的集成
- name: JAVA_TOOL_OPTIONS
value: -javaagent:/usr/skywalking/agent/skywalking-agent.jar
- name: SW_AGENT_NAME
value: chapter10-devops-demo
- name: SW_AGENT_COLLECTOR_BACKEND_SERVICES
# FQDN: servicename.namespacename.svc.cluster.local
value: oap.skywalking:11800
- name: SERVER_PORT
value: "8080"
- name: SPRING_PROFILES_ACTIVE
value: test
volumeMounts:
- mountPath: /usr/skywalking/agent
name: sw-agent
volumes:
- name: sw-agent
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: chapter10-monitor-demo
labels:
svc: chapter10-monitor-demo
spec:
selector:
app: chapter10-monitor-demo
ports:
- name: http
port: 8080
nodePort: 30001
type: NodePort
以上是掛載sidecar的k8s發布文件,以微服務“chapter10-devops-demo”為例,主要是通過共享volume的方式掛載agent。其中initContainers通過skywalking-agent卷掛載了skywalking-agent-sidecar鏡像中的/skywalking/agent,並將上面構建好的鏡像中的agent目錄cp到了/skywalking/agent目錄,完成之后微服務容器啟動時也掛載了skywalking-agent卷,並將其掛載到容器的/usr/skywalking/agent目錄,這樣就完成了共享過程。
這里有一個有意思的點,Java服務通過Agent接入SkyWalking一般情況下還需要在啟動命令中加入JVM參數,例如:“-javaagent:/usr/skywalking/agent/skywalking-agent.jar”。這就需要我們在定義Java程序鏡像打包的Dockerfile文件中通過“ENTRYPOINT”加入相關參數,例如:
ENTRYPOINT [ "sh", "-c", "java ${JAVA_OPTS} -javaagent:/app/agent/skywalking-agent.jar -Dskywalking.collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES} -Dskywalking.agent.service_name=${SW_AGENT_NAME} -Dskywalking.agent.instance_name=${HOSTNAME} -Djava.security.egd=file:/dev/./urandom -jar /app/app.jar $PROFILE"
但這種方式需要在Dockerfile文件中額外設置SkyWalking Agent相關的JVM參數,所以你可能沒注意到,在上述k8s部署文件中我所使用的是“JAVA_TOOL_OPTIONS”這個參數,而不是最常見的“JAVA_OPTS”。這個點很多人都不知道,如果你耐心看到這里,恭喜你Get了一個新技能!至於二者的區別,感興趣的朋友可以搜索下!
4)、部署啟動微服務,並驗證其是否已經正常接入SkyWalking監控
接下來我們進入部署文件所在目錄,執行發布命令如下:
$ kubectl apply -f deploy-skywalking.yml
deployment.apps/chapter10-monitor-demo created
service/chapter10-monitor-demo created
之后查看相關Pod是否運行成功:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
chapter10-monitor-demo-5767d54f5-vfqqf 1/1 Running 0 96m
運行成功了!此時可以訪問下服務的測試接口,多刷幾次,之后通過SkyWalking UI查看是否有監控數據,如下圖所示:
如上圖所示,在訪問微服務測試接口后可以看到SpringCloud微服務已經通過Agent像SkyWalking上報了APM監控數據!
后記
本文實驗步驟比較多,也許你很難一次性看完,但是真正的技術都是要練的,所以有空的時候搭建環境后玩一玩是理解文章內容的關鍵!
寫在最后
歡迎大家關注我的公眾號【風平浪靜如碼】,海量Java相關文章,學習資料都會在里面更新,整理的資料也會放在里面。
覺得寫的還不錯的就點個贊,加個關注唄!點關注,不迷路,持續更新!!!