云计算课程实验
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

537 行
18 KiB

1年前
  1. # 基于 ucloud 镜像仓库实现 GitHub 的 CI/CD
  2. GitHub Actions是GitHub的一个持续集成和持续交付(CI/CD)的平台,可以自动化构建、测试和部署Pipelines。
  3. #### Workflow(工作流)
  4. Workflow其实就是一个可配置的自动化过程,会运行一个或多个Job。Workflow定义在仓库的 .github/workflows文件夹的一个YML或者YAML文件中,并在仓库的Event触发时运行,也可以配置成手动触发运行,或者定时触发。一个Workflow中甚至可以引用另一个Workflow。
  5. 可以简单理解,一个YML配置文件就是一个Workflow,仓库可以在.github/workflows文件夹下拥有多个YML文件,即拥有多Workflows。每个Workflow可以执行一组不同的步骤。例如,一个仓库中有两个Workflows,就可以有一个Workflow用来构建和测试PR(Pull Request),另一个Workflow用来每次发布Release版本时自动部署应用。
  6. #### Event(事件)
  7. Event是仓库中的一个用来触发Workflow运行的特殊活动,有不同类型,如某人创建了一个PR、新建了一个Issue等,这些都是可以用来触发Workflow运行的Event。
  8. #### Job(工作)
  9. Job是Workflow中的一组步骤,在同一个Runner(运行者)上运行。每个步骤要么是将执行的Shell脚本,要么是将运行的Action。各步骤是按顺序执行的,并且相互依赖。由于各步骤是在同一个Runner上运行,因此可以将数据从一个步骤共享到另一个步骤。可以配置Job与其他Job的依赖关系;默认情况下,Job之间没有依赖关系,并且彼此并行运行。当一个Job依赖于另一个Job时,它将等待从属Job完成,然后才能运行。
  10. #### Action
  11. Action是GitHub Actions平台的自定义应用程序,用于执行复杂但经常重复的任务。Action可以减少在Workflow的YML文件中编写的重复代码。Action可以从GitHub上拉取仓库代码,为构建环境设置正确的工具链。我们可以编写自己的Action,也可以在GitHub Marketplace中寻找适合使用的Action。
  12. #### Runner(运行者)
  13. Runner是在Workflow被触发时运行它们的服务器。每个Runner一次可以运行一个Job。GitHub提供了Ubuntu Linux、Windows和macOS Runner来运行Workflow。每个Workflow都在全新的预先配置好的虚拟机中执行。如果我们需要不同的操作系统或者特定的硬件配置,可以用自己托管的Runner来代替GitHub提供的Runner。
  14. ## 实验目的和实验环境
  15. #### 【实验目的】
  16. (1)了解 GitHub Actions。
  17. (2)初步了解 Workflow 语法并自行构建。
  18. (3)实现 Spring Cloud 项目全自动化打包部署。
  19. (4)熟悉整个 CI/CD 的原理。
  20. #### 【实验环境】
  21. (1)CentOS:7.9 及以上
  22. (2)Docker:20.10.7 版。
  23. (3)docker-compose:1.29.2 版,build 5becea4c。
  24. (4)maven 3.9.5
  25. (5)java openjdk-1.8.0
  26. (6)本次实验建议使用 vscode 连接云主机,这样就可以直接在云主机上编辑,而不用总是上传文件。(这个可以自行上网学习,很方便,参考文档 https://blog.csdn.net/m0_49448331/article/details/126030161)
  27. ## 实验步骤
  28. #### 1.复制项目代码
  29. 将 https://github.com/OpenEduTech/DaseDevOps/tree/master 复制到自己的账户下后,会生成DaseDevops仓库。注意:下面所有图中显示的devops_demo仓库其实对应的是DaseDevops仓库下的dasedevops_spring_demo项目。
  30. ![image-20231111152635356](./images/openedu_repo.png)
  31. 随后,在自己的云主机任意目录下(我这里是 /root )执行 git clone 克隆仓库。使用 ssh 克隆。在克隆之前,可能会报没有 git 命令的错误,需要你手动安装一下 `yum install -y git`
  32. 请重点检查:是 fork 后的仓库,而不是 OpenEduTech 的仓库!!!不然会无法 push
  33. ![image-20231112145610434](./images/clone_my_repo.png)
  34. 在云主机上,生成本机密钥,用于免密认证。引号内的部分需要更换成你自己 github 绑定的邮箱。
  35. ```
  36. cd .ssh
  37. ssh-keygen -t rsa -C "102155014xx@stu.ecnu.edu.cn"
  38. ```
  39. 一路回车,待成功生成密钥后,执行命令 `cat id_rsa.pub` 将打印出的公钥复制到 github 中。
  40. 在 github 上配置云主机的 ssh key,点击头像,
  41. ![image-20231112145800806](./images/ssh_key.png)
  42. 密钥 Title 设置为 action,内容复制进去,这样就可以直接在云主机上免密 git push 了。
  43. #### 2.建立服务器A环境
  44. 这里使用的是GitHub托管的Runner,因此服务器A的环境其实已经由GitHub处理好了,不需要我们做任何操作。如果使用自托管的Runner,感兴趣的同学可以参考相关网页。
  45. #### 3.建立服务器B环境
  46. 首先,创建一个 centOS 云主机,1 核 2G 配置,20M 带宽流量计费,数据盘设置为 50 G
  47. 安装Docker(参照https://gitea.shuishan.net.cn/xslu_dase_ecnu_edu_cn/cloud-computing-course/src/branch/master/Assignment2.md),
  48. 安装docker-compose。
  49. ##### docker-compose 安装过程:
  50. 通过 curl 下载 docker compose
  51. ```bash
  52. 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
  53. ```
  54. 添加可执行权限
  55. ```bash
  56. sudo chmod +x /usr/local/bin/docker-compose
  57. ```
  58. 将文件 copy 到 /usr/bin/ 目录下
  59. ```bash
  60. sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
  61. ```
  62. 查看版本
  63. ```
  64. docker-compose --version
  65. ```
  66. 显示以下信息说明安装成功。
  67. ![image-20231110221616117](./images/docker_compose.png)
  68. ##### 安装 jdk
  69. 请注意,本次实验仅支持手动安装,不支持使用 yum 安装!!
  70. 浏览器访问 oracle 官网 https://www.oracle.com/java/technologies/downloads/#license-lightbox 下拉到 Java SE8,下载 x64 版本的压缩包
  71. ![image-20231112152329365](./images/java8.png)
  72. 在云主机的 /usr 目录下创建 java/ 文件夹
  73. ```
  74. cd /usr
  75. mkdir java
  76. cd java
  77. ```
  78. 把下载好的文件上传到 /usr/java 下
  79. 因为文件有 100M 大小,建议等待一段时间,等上传完成以后进行后续操作。
  80. 解压
  81. ```
  82. tar -zxvf jdk-8u391-linux-x64.tar.gz
  83. ```
  84. 解压以后,配置环境变量
  85. ```
  86. vi /etc/profile
  87. ```
  88. 在文件末尾添加
  89. ```
  90. export JAVA_HOME=/usr/java/jdk1.8.0_391
  91. export PATH=$JAVA_HOME/bin:$PATH
  92. export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib
  93. ```
  94. 更新环境变量
  95. ```
  96. source /etc/profile
  97. ```
  98. 输入 java -version 应该可以看到安装成功的消息。
  99. ![image-20231112155129640](./images/java_version.png)
  100. ##### 安装 maven
  101. 浏览器访问 maven 的下载页面 https://maven.apache.org/download.cgi
  102. 点击链接下载
  103. ![image-20231112145016939](./images/maven.png)
  104. 之后上传到 ucloud 云主机的 /usr/local 目录下,进入 /usr/local 目录,解压
  105. ```bash
  106. tar -xvf apache-maven-3.9.5-bin.tar.gz
  107. ```
  108. 执行 `vi /etc/profile` 配置环境变量
  109. ```
  110. MAVEN_HOME=/usr/local/apache-maven-3.9.5
  111. export PATH=${MAVEN_HOME}/bin:${PATH}
  112. ```
  113. 重载环境变量
  114. ```
  115. source /etc/profile
  116. ```
  117. 查看是否安装成功,输入 `mvn -v` 命令,显示相关信息
  118. ![image-20231112151248101](./images/maven_version.png)
  119. 由于 maven 官方源在国内速度比较慢,我们需要配置阿里 maven 源。cd 到 maven 的安装目录下
  120. 修改 conf 文件夹下的 settings.xml
  121. ```
  122. sudo vi conf/settings.xml
  123. ```
  124. 修改 mirrors 下没有被注释的那一部分
  125. ![image-20231112165227354](./images/maven_mirror.png)
  126. 我这边是已经修改过的,同学们需要根据以下代码修改原有的代码
  127. ```
  128. <mirror>
  129. <id>alimaven</id>
  130. <mirrorOf>central</mirrorOf>
  131. <name>aliyun maven</name>
  132. <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
  133. </mirror>
  134. ```
  135. 需要把原有的 `<block>true</block>` 去掉
  136. #### 4.建立 ~~ucloud~~ 阿里云容器镜像服务
  137. 本来是想通过 ucloud 使用容器镜像服务的,但是奈何 ucloud 镜像仓库无法通过 github action 的 workflow 进行登录并且操作。无奈之下只能使用阿里云的 [aliyun/acr-login](https://github.com/aliyun/acr-login) 进行 yml 文件登录到 docker 的操作。
  138. 为什么不能通过 ucloud 创建容器镜像服务然后再通过登录阿里云镜像仓库然后把镜像推送到 ucloud 中呢?不行。就像 github 中无法直接把自己的代码推送到别人的仓库中一样。
  139. 所以为了完成本次实验,需要麻烦大家再在阿里云上创建一个容器镜像服务。
  140. 首先,访问 https://www.aliyun.com/ 点击右上角的 “注册/登录” 按钮,完成注册与登录操作
  141. ![image-20231111104809589](./images/aliyunlogin.png)
  142. 可以直接使用支付宝扫码登录
  143. 登录成功后,访问实例列表 https://cr.console.aliyun.com/cn-shanghai/instances 点击创建个人实例。
  144. 需要通过 RAM 授权,按照流程点击确认即可。
  145. ![image-20231111105335421](./images/instances.png)
  146. ![image-20231111105406205](./images/personal_instance.png)
  147. 创建完个人版实例以后,需要设置 registry 登录密码。
  148. 然后点击 命名空间 -> 创建命名空间 -> 给自己的命名空间起一个名字吧
  149. 推荐使用自己的姓名拼音之间加 "-" 号进行命名。如果出现重名,就再加一些符号。
  150. ![image-20231111105841358](./images/namespace.png)
  151. ![image-20231112155904281](./images/personal_page.png)
  152. ### **需要把 默认仓库类型 设置为公开。**
  153. ## `作业 1: 创建完成以后,进入个人实例-命名空间页面,截图插入实验报告。`
  154. #### 5.接下来,编写本项目的 workflow 文件。在项目根目录下新建 .github/workflows 文件夹将以下代码保存在 docker-publish.yml
  155. ```yml
  156. name: SpringCloud CI/CD with Docker
  157. on:
  158. push:
  159. branches: [ master ]
  160. pull_request:
  161. branches: [ master ]
  162. env:
  163. MODULE_1: gateway
  164. MODULE_2: hello
  165. MODULE_3: login
  166. MODULE_4: provider_one
  167. MODULE_5: provider_two
  168. MODULE_6: provider_three
  169. jobs:
  170. build:
  171. runs-on: ubuntu-latest
  172. steps:
  173. - uses: actions/checkout@v2
  174. - name: Set up JDK 8
  175. uses: actions/setup-java@v2
  176. with:
  177. distribution: 'temurin'
  178. java-version: '8'
  179. cache: 'maven'
  180. - name: Build with Maven
  181. run: |
  182. whoami
  183. pwd
  184. cd dasedevops_spring_demo
  185. mvn -B package --file pom.xml
  186. pwd
  187. - name: Login to Aliyun Container Registry (ACR)
  188. uses: aliyun/acr-login@v1
  189. with:
  190. login-server: https://registry.cn-shanghai.aliyuncs.com
  191. region-id: cn-shanghai # 3
  192. username: "${{ secrets.ACR_USERNAME }}"
  193. password: "${{ secrets.ACR_PASSWORD }}"
  194. - name: Build and push image
  195. run: |
  196. docker build -t ${{ secrets.ALI_NAMESPACE_URL }}/$MODULE_1:latest ./dasedevops_spring_demo/$MODULE_1
  197. docker push ${{ secrets.ALI_NAMESPACE_URL }}/$MODULE_1:latest
  198. docker build -t ${{ secrets.ALI_NAMESPACE_URL }}/$MODULE_2:latest ./dasedevops_spring_demo/$MODULE_2
  199. docker push ${{ secrets.ALI_NAMESPACE_URL }}/$MODULE_2:latest
  200. docker build -t ${{ secrets.ALI_NAMESPACE_URL }}/$MODULE_3:latest ./dasedevops_spring_demo/$MODULE_3
  201. docker push ${{ secrets.ALI_NAMESPACE_URL }}/$MODULE_3:latest
  202. docker build -t ${{ secrets.ALI_NAMESPACE_URL }}/$MODULE_4:latest ./dasedevops_spring_demo/$MODULE_4
  203. docker push ${{ secrets.ALI_NAMESPACE_URL }}/$MODULE_4:latest
  204. docker build -t ${{ secrets.ALI_NAMESPACE_URL }}/$MODULE_5:latest ./dasedevops_spring_demo/$MODULE_5
  205. docker push ${{ secrets.ALI_NAMESPACE_URL }}/$MODULE_5:latest
  206. docker build -t ${{ secrets.ALI_NAMESPACE_URL }}/$MODULE_6:latest ./dasedevops_spring_demo/$MODULE_6
  207. docker push ${{ secrets.ALI_NAMESPACE_URL }}/$MODULE_6:latest
  208. - name: Copy single file to remote
  209. uses: garygrossgarten/github-action-scp@release
  210. with:
  211. local: ./dasedevops_spring_demo/docker-compose.yml
  212. remote: scp/devops_demo/docker-compose.yml
  213. host: ${{ secrets.HOST }}
  214. username: ${{ secrets.USER_NAME }}
  215. password: ${{ secrets.USER_PASSWORD }}
  216. port: ${{ secrets.PORT }}
  217. depoly:
  218. needs: [ build ]
  219. name: Docker Pull And Docker-compose Run
  220. runs-on: ubuntu-latest
  221. steps:
  222. - name: executing remote ssh commands using password
  223. uses: appleboy/ssh-action@master
  224. with:
  225. host: ${{ secrets.HOST }}
  226. username: ${{ secrets.USER_NAME }}
  227. password: ${{ secrets.USER_PASSWORD }}
  228. port: ${{ secrets.PORT }}
  229. script: |
  230. docker-compose -f scp/devops_demo/docker-compose.yml up -d
  231. docker-compose -f scp/devops_demo/docker-compose.yml stop
  232. docker-compose -f scp/devops_demo/docker-compose.yml pull
  233. docker-compose -f scp/devops_demo/docker-compose.yml up -d
  234. docker image prune -f
  235. ```
  236. 相关 secrets 解释:
  237. 为什么需要用到 github secrets? 我们都知道,很多情况下,项目有一些隐秘信息,不能直接配置在项目内,包括但不仅限于: github token, 各种账号的用户名密码,私钥信息,各种网站的 api key, app key, secret key 等等。这时候就需要用到 secrets。所以根据 secrets 的特性,一经设置就不可查看!所以设置 secrets 时务必仔细确认!
  238. secrets.ALI_NAMESPACE_URL: 对应阿里云容器镜像服务的 “所在地域 + 命名空间” ,我这里是 “registry.cn-shanghai.aliyuncs.com/easonlin-devops-practice” ,前面的 registry.cn-shanghai.aliyuncs.com 是地域,后面的 easonlin-devops-practice 是命名空间。地域和命名空间很重要,在我们登录阿里云 registry 和拉取、推送镜像时都会用到。
  239. ![image-20231111163606733](./images/detail_info.png)
  240. secrets.ACR_USERNAME:阿里云容器服务registry的用户名。
  241. secrets.ACR_PASSWORD:阿里云容器服务registry的用户密码。
  242. secrets.HOST:服务器B的IP地址,就是 ucloud 云主机的公有 ip
  243. secrets.USER_NAME:服务器B的登录用户名,默认为 root
  244. secrets.USER_PASSWORD:服务器B的登录密码。
  245. secrets.PORT:服务器B的SSH开放端口,默认是22。
  246. 这些 secrets都可以在代码仓库的“Setting →Secrets → Actions”界面进行设置,使用自己账号的阿里云容器镜像服务中的参数。
  247. ![image-20231111161358868](./images/secrets.png)
  248. 新建 secret:点击 New repository secret 按钮
  249. ![image-20231111161513709](./images/new_secret.png)
  250. 输入 secret name 与 value 即可
  251. ![image-20231111161537636](./images/new_secret_detail.png)
  252. 随后,进入微服务文件夹 dasedevops_spring_demo 下,进入以下六个文件夹的 Dockerfile 进行编辑。
  253. <img src="./images/files.png" alt="files.png" style="zoom:50%;" />
  254. 注意到了吗,这六个文件夹就和 workflow 中的六个 MODULE 命名相同。
  255. ```
  256. env:
  257. MODULE_1: gateway
  258. MODULE_2: hello
  259. MODULE_3: login
  260. MODULE_4: provider_one
  261. MODULE_5: provider_two
  262. MODULE_6: provider_three
  263. ```
  264. 编辑 DockerFile 也是为了分别将这六个 module 打包成镜像,上传到镜像仓库中。这么看,这个 yml 文件是不是也不那么复杂?🐶
  265. gateway微服务的Dockerfile文件内容如下:
  266. **在六个文件夹下分别创建名为dockerfile的文件**
  267. ```dockerfile
  268. FROM openjdk:8
  269. MAINTAINER ningzhicheng
  270. VOLUME /tmp
  271. ADD ./target/*.jar /gateway.jar
  272. ENTRYPOINT ["java","-jar","/gateway.jar"]
  273. EXPOSE 8080
  274. ```
  275. hello微服务的Dockerfile文件内容如下:
  276. ```dockerfile
  277. FROM openjdk:8
  278. MAINTAINER ningzhicheng
  279. VOLUME /tmp
  280. ADD ./target/*.jar /hello.jar
  281. ENTRYPOINT ["java","-jar","/hello.jar"]
  282. EXPOSE 8001
  283. ```
  284. login微服务的Dockerfile文件内容如下:
  285. ```dockerfile
  286. FROM openjdk:8
  287. MAINTAINER ningzhicheng
  288. VOLUME /tmp
  289. ADD ./target/*.jar /login.jar
  290. ENTRYPOINT ["java","-jar","/login.jar"]
  291. EXPOSE 8000
  292. ```
  293. provider_one微服务的Dockerfile文件内容如下:
  294. ```dockerfile
  295. FROM openjdk:8
  296. MAINTAINER ningzhicheng
  297. VOLUME /tmp
  298. ADD ./target/*.jar /provider_one.jar
  299. ENTRYPOINT ["java","-jar","/provider_one.jar"]
  300. EXPOSE 8666
  301. ```
  302. provider_two微服务的Dockerfile文件内容如下:
  303. ```dockerfile
  304. FROM openjdk:8
  305. MAINTAINER ningzhicheng
  306. VOLUME /tmp
  307. ADD ./target/*.jar /provider_two.jar
  308. ENTRYPOINT ["java","-jar","/provider_two.jar"]
  309. EXPOSE 8667
  310. ```
  311. provider_three微服务的Dockerfile文件内容如下:
  312. ```dockerfile
  313. FROM openjdk:8
  314. MAINTAINER ningzhicheng
  315. VOLUME /tmp
  316. ADD ./target/*.jar /provider_three.jar
  317. ENTRYPOINT ["java","-jar","/provider_three.jar"]
  318. EXPOSE 8668
  319. ```
  320. #### 6.编写 docker-compose.yml 文件
  321. 在 dasedevops_spring_demo 下新建一个 docker-compose.yml 文件,将以下内容复制到文件中
  322. ```dockerfile
  323. version: "3"
  324. services:
  325. providerOne:
  326. image: ${{ secrets.ALI_NAMESPACE_URL }}/provider_one:latest
  327. ports:
  328. - "8666:8666"
  329. gateway:
  330. image: ${{ secrets.ALI_NAMESPACE_URL }}/gateway:latest
  331. ports:
  332. - "8080:8080"
  333. login:
  334. image: ${{ secrets.ALI_NAMESPACE_URL }}/login:latest
  335. ports:
  336. - "8000:8000"
  337. hello:
  338. image: ${{ secrets.ALI_NAMESPACE_URL }}/hello:latest
  339. ports:
  340. - "8001:8001"
  341. providerTwo:
  342. image: ${{ secrets.ALI_NAMESPACE_URL }}/provider_two:latest
  343. ports:
  344. - "8667:8667"
  345. providerThree:
  346. image: ${{ secrets.ALI_NAMESPACE_URL }}/provider_three:latest
  347. ports:
  348. - "8668:8668"
  349. ```
  350. 文件目录如下:
  351. ![image](./images/schema.png)
  352. 随后将所有文件提交到暂存区,创建提交信息,推送提交。
  353. ```
  354. git add .
  355. git commit -m "test"
  356. git push
  357. ```
  358. 查看自己仓库的 Actions 界面
  359. ![image-20231112165619307](./images/my_action.png)
  360. ## `作业 2: 将成功的截图(示例如下)插入到实验报告中。`
  361. ![image-20231112165720555](./images/action_detail.png)
  362. ## `作业 3:将阿里云容器镜像服务-个人实例-镜像仓库页面截图,插入实验报告。`
  363. 示例如下图,必须包括这六个仓库。
  364. ![image-20231112170627251](./images/my_image_repo.png)
  365. ## `本次实验已结束,请同学们立即释放所有 ucloud 云主机资源!`