第一章 DevOps概念
一、什么是DevOps
DevOps是一种思想或方法论,它涵盖开发、测试、运维的整个过程(不仅限于Java)。DevOps强调软件开发人员与软件测试、软件运维、质量保障(QA)部门之间有效的沟通与协作。强调通过自动化的方法管理软件变更、软件集成。使软件从构建到测试、发布更加快捷、可靠,最终按时交付软件。
二、为什么当今大公司一定要使用DevOps
2.1 传统瀑布模型
- 完整、清晰、固定的需求
- 完整、清晰、固定的产品定义
2.2 敏捷开发模型
- 需求频繁变化
- 需要快速开发
2.3 DevOps开发模型
- 需求频繁变化
- 开发需要敏捷
- 操作需要敏捷
DevOps这种软件开发方法,涉及到软件整个开发生命周期,这些活动只能在DevOps中实现,而不是敏捷或瀑布。DevOps是在较短的开发周期内开发高质量软件的首选方法,同时可以提高客户满意度。这就是为什么顶级互联网公司选择DevOps作为其业务目标的前进方向。
三、如何落地实现DevOps这种理念
DevOps兴起于2009年,近年来由于云计算、互联网的发展,促进了DevOps的基础设施及工具的发展,涌现出了一大批优秀的工具,这些工具包括开发、测试、运维的各个领域,例如:GitHub、Git\SVN、Docker、Jenkins、Hudson、Ant\Maven\Gradle、Selenium、QUnit、JMeter等等。下图是DevOps相关的工具集
四、Docker一把瑞士军刀,让DevOps真正落地于实践
Docker是一个开源的应用容器引擎,诞生于2013年初,基于Go语言开发,dotCloud公司出品(后改名为Docker Inc)。
Docker可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux机器上。容器是完全使用沙箱机制,相互隔离。容器性能开销极低。
Docker从17.03版本之后分为CE(Community Edition,社区版)和EE(Enterprise Edition,企业版)。
镜像(Image): Docker镜像(Image),就相当于是一个root文件系统,比如官方镜像Ubuntu:16.04就包含了完整的一套Ubuntu16.04最小系统的root文件系统。
容器(Container): 镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和对象一样,镜像是静态的定义,容器是镜像运行时的实体,容器可以被创建、启动、停止、删除、暂停等。
仓库(Repository): 仓库可以看成一个代码控制中心,用来保存镜像。
五、持续集成流程
第二章 DevOps实践
一、DevOps基础环境安装
1.1 使用系统说明
远程服务器:阿里云ECS,2核16G,Ubuntu 18.04.64位 (服务器配置最少2核8G起步),IP地址
120.24.95.76
阿里云ECS安全组开放端口:
5000
、6001
、6002
、6003
、8080
、8001
本地电脑:Mac OS
1.2 安装Git
1.2.1 ssh登录ECS
AC-2:~ AC$ ssh root@120.24.95.76
root@120.24.95.76's password:
# 按提示输入密码进行登录
1.2.2 安装Git
更新服务器上的包索引
sudo apt-get update
安装git
sudo apt-get install git
1.2.3 配置Git全局环境
git config --global user.name "alanchen"
git config --global user.email "chenyan900520@126.com"
1.2.4 生成ssh密钥
ssh-keygen -t rsa -C "chenyan900520@126.com"
1.2.5 查看拷贝公钥
如果后面需要在GitLab配置SSH Keys
时,可以从这里拷贝公钥,当然也可以用账号密码进行操作
cd ~/.ssh
cat id_rsa.pub
1.3 安装Maven
1.3.1 安装步骤
1、首先检查是否有maven
mvn -v
2、search
apt-cache search maven
3、安装
sudo apt-get install maven
4、检查是否安装成功
mvn -v
安装成功会显示版本号
root@iZwz96ew7wfkgebgbq9crbZ:~# mvn -v
Apache Maven 3.6.0
Maven home: /usr/share/maven
Java version: 11.0.13, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "4.15.0-166-generic", arch: "amd64", family: "unix"
root@iZwz96ew7wfkgebgbq9crbZ:~#
1.3.2 配置maven国内镜像,提高下载速度
1、找到配置文件settings.xml
位置
通过mvn -v
可以显示maven的安装目录,在安装目录下就有settings.xml
配置文件。
cd /usr/share/maven/conf
2、编辑settings.xml
配置文件
vi settings.xml
3、修改settings.xml
配置文件
找到<mirrors></mirrors>标签,在这个标签中加入国内的镜像即可,这里我推荐maven阿里云中央仓库。
<mirrors>
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
1.4 安装Docker
1.4.1 安装
使用官方安装脚本自动安装,安装命令如下:
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
也可以使用国内 daocloud 一键安装命令:
curl -sSL https://get.daocloud.io/docker | sh
1.4.2 配置镜像加速器(非必要)
二、安装Docker私有仓库
2.1 安装步骤
1、搜索镜像
docker search registry
2、拉取镜像
docker pull registry
3、创建容器
#创建存放镜像的目录
mkdir -p /opt/data/docker/
#创建容器 -p指定端口 -v数据卷挂载
docker run -it -d -p 5000:5000 -v /opt/data/docker:/tmp/registry registry
ECS安全组需要开放5000
端口
2.2 配置私有仓库地址
vi /etc/docker/deamon.json
配置内容
{
“insecure-registries”:[“120.24.95.76:5000”]
}
2.3 重启
1、重启配置
sudo systemctl daemon-reload
2、重启Docker
sudo systemctl restart docker
3、查看仓库容器状态
docker ps -a
仓库容器当前为退出状态
root@iZwz96ew7wfkgebgbq9crbZ:/usr/share/maven/conf# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
22db63715a88 registry "/entrypoint.sh /etc…" 4 minutes ago Up 4 minutes 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp thirsty_euclid
root@iZwz96ew7wfkgebgbq9crbZ:/usr/share/maven/conf#
4、启动本地仓库容器
docker start 22db63715a88
启动
root@iZwz96ew7wfkgebgbq9crbZ:/usr/share/maven/conf# docker start 22db63715a88
22db63715a88
root@iZwz96ew7wfkgebgbq9crbZ:/usr/share/maven/conf#
2.4 访问Docker私有仓库
在浏览器中访问http://120.24.95.76:5000/v2/_catalog
三、安装GitLab
下面将通过Docker安装GitLab,如果在ECS宿主机上直接安装GitLab,可参考我另外一篇文章 Ubuntu部署GitLab
3.1 Docker安装GitLab步骤
1、查找GitLab镜像
docker search gitlab
2、拉取GitLab镜像
docker pull gitlab/gitlab-ce
3、查看本地镜像
docker images
root@iZwz96ew7wfkgebgbq9crbZ:/usr/share/maven/conf# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
gitlab/gitlab-ce latest 508bfaaaf273 4 days ago 2.36GB
registry latest b8604a3fe854 2 months ago 26.2MB
root@iZwz96ew7wfkgebgbq9crbZ:/usr/share/maven/conf#
4、建立映射文件
在本机建立3个目录,分别为:配置文件、数据文件、日志文件。GitLab容器通过挂载本机目录启动后就可以映射到本机。主要出于以下两点原因:
1、后续可以直接在本机查看和编辑,不用再进入容器操作。
2、容器是一个虚拟化的技术,容器挂了之后,容器里的数据就没了,数据不安全
创建文件:
# 配置文件
mkdir -p /home/gitlab/etc
# 数据文件
mkdir -p /home/gitlab/data
# 日志文件
mkdir -p /home/gitlab/logs
5、启动容器
docker run --name='gitlab' -d \
--publish 6002:443 --publish 6001:80 --publish 6003:22 \
-v /home/gitlab/etc:/etc/gitlab \
-v /home/gitlab/data:/var/opt/gitlab \
-v /home/gitlab/logs:/var/log/gitlab \
gitlab/gitlab-ce:latest
https
443端口用6002端口映射,web访问端口80用6001端口映射(GitLab默认用的是80端口),ssh
端口22用6003端口映射。(备注ECS阿里云要开放6001、6002、6003端口)
root@iZ0jldusfvkkz0zfl9gsdaZ:/home/gitlab/etc# docker run --name='gitlab' -d \
> --publish 6002:443 --publish 6001:80 --publish 6003:22 \
> -v /home/gitlab/etc:/etc/gitlab \
> -v /home/gitlab/data:/var/opt/gitlab \
> -v /home/gitlab/logs:/var/log/gitlab \
> gitlab/gitlab-ce:latest
56a55cb01c9df7c9a5bee514b04adf131b158ae83c44fedf9570c469a6328f75
可以通过docker logs -f gitlab
命令查看启动日志,通过docker ps -a
查看容器启动状态。
root@iZwz96ew7wfkgebgbq9crbZ:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
02bde20ab6df gitlab/gitlab-ce:latest "/assets/wrapper" 2 minutes ago Up 2 minutes (health: starting) 0.0.0.0:6003->22/tcp, :::6003->22/tcp, 0.0.0.0:6001->80/tcp, :::6001->80/tcp, 0.0.0.0:6002->443/tcp, :::6002->443/tcp gitlab
22db63715a88 registry "/entrypoint.sh /etc…" 5 hours ago Up 5 hours 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp thirsty_euclid
root@iZwz96ew7wfkgebgbq9crbZ:~#
状态为health: starting
表示还在启动中。
6、访问GitLab
http://120.24.95.76:6001
3.2 修改root默认密码
3.2.1 查看容器,GitLab是否正常运行
docker ps -a
Up为启动状态
root@iZwz96ew7wfkgebgbq9crbZ:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
02bde20ab6df gitlab/gitlab-ce:latest "/assets/wrapper" 6 minutes ago Up 6 minutes (healthy) 0.0.0.0:6003->22/tcp, :::6003->22/tcp, 0.0.0.0:6001->80/tcp, :::6001->80/tcp, 0.0.0.0:6002->443/tcp, :::6002->443/tcp gitlab
22db63715a88 registry "/entrypoint.sh /etc…" 5 hours ago Up 5 hours 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp thirsty_euclid
root@iZwz96ew7wfkgebgbq9crbZ:~#
3.2.2 进入GitLab的容器中
# docker exec -it(gitlab的容器名称或id) bash
docker exec -it 63cc520288bd bash
// 或者
docker exec -it gitlab bash
root@iZwz96ew7wfkgebgbq9crbZ:~# docker exec -it gitlab bash
root@02bde20ab6df:/#
3.2.3 修改密码
1、使用以下命令启动Ruby on Rails控制台
gitlab-rails console
2、等待控制台加载完毕并找到root用户,稍微要多等待一会
user = User.where(id: 1).first
或者
user = User.find_by(email: 'admin@example.com')
3、更改密码
user.password='123456abc'
user.password_confirmation='123456abc'
4、保存更改
user.save
操作过程:
root@iZwz96ew7wfkgebgbq9crbZ:~# docker exec -it gitlab bash
root@02bde20ab6df:/# gitlab-rails console
--------------------------------------------------------------------------------
Ruby: ruby 2.7.5p203 (2021-11-24 revision f69aeb8314) [x86_64-linux]
GitLab: 14.6.2 (0a901d60f8a) FOSS
GitLab Shell: 13.22.1
PostgreSQL: 12.7
--------------------------------------------------------------------------------
Loading production environment (Rails 6.1.4.1)
irb(main):001:0> user = User.where(id: 1).first
=> #<User id:1 @root>
irb(main):002:0> user = User.find_by(email: 'admin@example.com')
=> #<User id:1 @root>
irb(main):003:0> user.password='123456abc'
=> "123456abc"
irb(main):004:0> user.password_confirmation='123456abc'
=> "123456abc"
irb(main):005:0> user.save
=> true
irb(main):006:0>
5、用root账号以及刚设置的密码登录GitLab
6、登录成功
3.3 配置
按上面的方式,GitLab容器运行没问题,但在GitLab上创建项目的时候,生成项目的URL访问地址是按容器的hostname
来生成的,也就是容器id(如:http://02bde20ab6df/root/test.git
)。作为GitLab服务器,我们需要一个固定的URL访问地址,于是需要配置gitlab.rb
(宿主机路径:/home/gitlab/comfig/gitlab.rb
)
3.3.1 配置gitlab.rb
cd /home/gitlab/etc
vi gitlab.rb
可以使用/
来查找关键字,找到指定的内容,然后通过n来下一个查找,编辑内容:
# 在GitLab创建项目时候http地址的host(如果不加端口,端口默认为80)
external_url 'http://120.24.95.76:6001'
# 在GitLab创建项目时候ssh地址的host(不用添加端口,不用加http)
gitlab_rails['gitlab_ssh_host'] = '120.24.95.76'
# Docker run 的时候我们把22端口映射为外部的6003了,这里修改下
gitlab_rails['gitlab_shell_ssh_port'] = 6003
跳坑重点强调:
22端口映射问题:如果不映射22端口,ssh克隆时一直提示输入密码,且密码错误。ssh传输都是通过22端口传输的,一般被宿主的sshd服务占用。所以GitLab容器的22端口不能直接映射到宿主的22端口,要换个其他端口,比如6003。这样,通过端口映射,客户端的ssh传输请求就能达到容器中的GitLab服务。
external_url默认为80端口问题:请注意,我上面的配置
external_url
是有写端口号6001
的,如果不写,则默认为80端口。此处有两个坑:
第一个坑:如果external_url
不写端口6001
,而用默认的80
端口,那项目的Clone with HTTP
地址也是不带端口的(默认为80
端口),由于80
端口的原因,我们在git clone http 地址时会clone失败。即使在下面要修改的gitlab.yml
文件里,将port
端口设置为6001
,保存退出,当容器一启动再来看该文件内容,port
又会变成默认的80
端口。
第二个坑:如果external_url
写了端口6001
,重启配置,容器也能正常重启成功,但GitLab却访问不了了。问题的原因就出在external_url地址设置上。
GitLab默认的http访问端口号为80端口,如果想更改端口号,一般是通过docker run时设置端口映射,将80端口映射为其他端口。例如前面的操作:
docker run --name='gitlab' -d \
--publish 6002:443 --publish 6001:80 --publish 6003:22 \
-v /home/gitlab/etc:/etc/gitlab \
-v /home/gitlab/data:/var/opt/gitlab \
-v /home/gitlab/logs:/var/log/gitlab \
gitlab/gitlab-ce:latest
这里将GitLab的http端口改为6001,如果你这时修改external_url地址为http://ip:6001
,那GitLab肯定访问不了,因为你已经将内部的端口号修改为6001
端口了,而你通过docker run映射出来的端口号是80
端口,所以不可能访问到。那该怎么办?既然你已经将内部的端口号由80
端口改为6001
端口,这时候你就将容器停止并删除,启动容器时,将端口由80
改成6001
docker run --name='gitlab' -d \
--publish 6002:443 --publish 6001:6001 --publish 6003:22 \
-v /home/gitlab/etc:/etc/gitlab \
-v /home/gitlab/data:/var/opt/gitlab \
-v /home/gitlab/logs:/var/log/gitlab \
gitlab/gitlab-ce:latest
3.3.2 配置gitlab.yml
cd /home/gitlab/data/gitlab-rails/etc
vi gitlab.yml
gitlab:
## Web server settings (note: host is the FQDN, do not include http://)
host: 120.24.95.76
port: 6001
https: false
3.3.3 停用GitLab容器
docker stop gitlab
3.3.4 删除GitLab容器
docker rm -f gitlab
# 或者通过id删除
docker rm -f 0535d33bf034
3.3.5 启动GitLab容器
docker run --name='gitlab' -d \
--publish 6002:443 --publish 6001:6001 --publish 6003:22 \
-v /home/gitlab/etc:/etc/gitlab \
-v /home/gitlab/data:/var/opt/gitlab \
-v /home/gitlab/logs:/var/log/gitlab \
gitlab/gitlab-ce:latest
注意:两个端口都是6001
3.4 登录GitLab创建devops项目
http://120.24.95.76:6001
我们可以看到项目的访问地址为IP,不是容器ID了,SSH地址后面是6003
端口,HTTP地址后面是6001
端口。
3.5 GitLab创建用户
3.6 项目授权
将devops
项目授权给chenyan
用户
用chenyan
账号登录GitLab,即可以看到devops
项目
3.7 GitLab配置SSH Keys
为chenyan
用户配置SSH Keys
3.7.1 配置Git全局环境
在本地电脑操作
git config --global user.name "chenyan"
git config --global user.email "271961730@qq.com"
3.7.2 生成ssh密钥
ssh-keygen -t rsa -C "271961730@qq.com"
3.7.3 拷贝公钥
cd ~/.ssh
cat id_rsa.pub
AC-2:~ AC$ cd ~/.ssh
AC-2:.ssh AC$ cat id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC2kYvXyZRehV65k96uuVlfCxbQrlCTTtdYdSOm8ZOeywo6+vqWpQjVyDobeQKANOswOr09YR8UwRd5VjssLI7dh4SHuvvZpgWaPGZf1HxT9N5ssIUNTKLe36O5QXeQQ41wTF6nNRoFkWbErzzw+DqUTBYvVJGVpfrJMO4V/SFZk4WceQT1zJ2E3lBI6hjbtuIgGSqYK6trutuCnEw+DW7zkrTxzKQqVzB1h1m0dTpqT+rwl3j16avmyiKFWfL1uMvGJfeTP7XNEsTaQXo0eseByVxI8NNPguPihGPp0Rt7Bx7SUFam+2zhg6LdMqXwPiXft8DPI8YiC0zHvKpYC5fB 271961730@qq.com
AC-2:.ssh AC$
3.7.4 配置SSH Keys
用chenyan
账号登录GitLab
3.7.5 拉取项目到本地
进入本地电脑code
,用来存放devops项目
cd ~/code/
1、通过SSH方式clone代码
git clone ssh://git@120.24.95.76:6003/root/devops.git
AC-2:.ssh AC$ cd ~/code/
AC-2:code AC$ git clone ssh://git@120.24.95.76:6003/root/devops.git
Cloning into 'devops'...
The authenticity of host '[120.24.95.76]:6003 ([120.24.95.76]:6003)' can't be established.
ECDSA key fingerprint is SHA256:iwQBJ3+2tzc3oM1K4l8qaLg5Nl6UtM0EM8fXiNhpyyU.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[120.24.95.76]:6003' (ECDSA) to the list of known hosts.
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.
AC-2:code AC$
2、或者可以通过HTTP输入账号密码的方式clone代码
先将刚clone的代码删除
git clone http://120.24.95.76:6001/root/devops.git
AC-2:code AC$ git clone http://120.24.95.76:6001/root/devops.git
Cloning into 'devops'...
Username for 'http://120.24.95.76:6001': chenyan
Password for 'http://chenyan@120.24.95.76:6001':
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
AC-2:code AC$
四、编写devops项目代码并提交到远程仓库
4.1 创建SpringBoot项目devops
设置项目名称
添加Spring Web依赖
覆盖刚刚clone的项目目录
4.2 编写restful接口
编写controller
代码
@RestController
@RequestMapping("devops")
public class DevOpsController {
@GetMapping("hello")
public String hello(){
return "AlanChen";
}
}
自动生成的pom.xml
文件为
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.alanchen</groupId>
<artifactId>devops</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>devops</name>
<description>Demo project for DevOps</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
访问controller
服务接口
http://127.0.0.1:8080/devops/hello
4.3 将本地的devops项目代码提交到远程仓库
cd ~/code/devops
git add .
git commit -m "add hello"
git push
4.4 登录GitLab查看项目情况
五、Docker制作SpringBoot项目工程镜像容器
5.1 打包devops项目为jar
5.2 将jar包上传到阿里云ECS服务器
先在ECS服务器上,建立/data/docker-demo目录
mkdir -p /data/devops-demo
上传jar文件
scp /Users/AC/code/devops/target/devops-0.0.1-SNAPSHOT.jar root@120.24.95.76:/data/devops-demo/devops-0.0.1-SNAPSHOT.jar
5.3 编写Dockerfile文件
阿里云ECS,进入/data/devops-demo目录,编写Dockerfile文件
cd /data/devops-demo
vi Dockerfile
文件内容
# 基础镜像使用java
FROM java:8
# 作者
MAINTAINER eangulee chenyan900520@126.com
# VOLUME 指定了临时文件目录为/tmp。
# 其效果是在主机 /var/lib/docker 目录下创建了一个临时文件,并链接到容器的/tmp
VOLUME /tmp
# 将jar包添加到容器中并更名为app.jar
ADD *.jar app.jar
# 运行jar包
RUN bash -c 'touch /app.jar'
#指定容器启动时要执行的命令,但如果存在CMD指令,CMD中的参数会被附加到ENTRYPOINT指令的后面
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
VOLUME指定了临时文件目录为/tmp。其效果是在主机/var/lib/docker 目录下创建了一个临时文件,并链接到容器的/tmp。该步骤是可选的,如果涉及到文件系统的应用就很有必要了。/tmp目录用来持久化到Docker数据文件夹,因为Spring Boot使用的内嵌 Tomcat 容器默认使用/tmp作为工作目录项目的 jar 文件作为 app.jar添加到容器的ENTRYPOINT执行项目app.jar。为了缩短Tomcat启动时间,添加一个系统属性指向/dev/./urandom作为Entropy Source。如果是第一次打包,它会自动下载Java 8的镜像作为基础镜像,以后再制作镜像的时候就不会再下载了。
/data/devops-demo
目录文件如下
root@iZwz9doa4kcnsjcv8fgt8pZ:/data/devops-demo# ls
devops-0.0.1-SNAPSHOT.jar Dockerfile
root@iZwz9doa4kcnsjcv8fgt8pZ:/data/devops-demo#
5.4 制作镜像
在/data/devops-demo
目录下执行命令,-t 参数是指定此镜像的tag名
docker build -t devopsdemo .
注意:最后有一个点.
执行过程
root@iZwz9doa4kcnsjcv8fgt8pZ:/data/devops-demo# docker build -t devopsdemo .
Sending build context to Docker daemon 17.56MB
Step 1/6 : FROM java:8
8: Pulling from library/java
5040bd298390: Pull complete
fce5728aad85: Pull complete
76610ec20bf5: Pull complete
60170fec2151: Pull complete
e98f73de8f0d: Pull complete
11f7af24ed9c: Pull complete
49e2d6393f32: Pull complete
bb9cdec9c7f3: Pull complete
Digest: sha256:c1ff613e8ba25833d2e1940da0940c3824f03f802c449f3d1815a66b7f8c0e9d
Status: Downloaded newer image for java:8
---> d23bdf5b1b1b
Step 2/6 : MAINTAINER eangulee chenyan900520@126.com
---> Running in 614c9c58a554
Removing intermediate container 614c9c58a554
---> ec0fb00f60c5
Step 3/6 : VOLUME /tmp
---> Running in 6bc1644cad98
Removing intermediate container 6bc1644cad98
---> a808a7c8ff24
Step 4/6 : ADD *.jar app.jar
---> 92fa72757b8c
Step 5/6 : RUN bash -c 'touch /app.jar'
---> Running in 17dbf2e59690
Removing intermediate container 17dbf2e59690
---> 2929030bfdb3
Step 6/6 : ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
---> Running in 5cdb4c0029ec
Removing intermediate container 5cdb4c0029ec
---> f2149993ff24
Successfully built f2149993ff24
Successfully tagged devopsdemo:latest
root@iZwz9doa4kcnsjcv8fgt8pZ:/data/devops-demo#
制作完成后通过docker images
命令查看我们制作的镜像
root@iZwz9doa4kcnsjcv8fgt8pZ:/data/devops-demo# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
devopsdemo latest f2149993ff24 About a minute ago 678MB
gitlab/gitlab-ce latest 508bfaaaf273 4 days ago 2.36GB
registry latest b8604a3fe854 2 months ago 26.2MB
java 8 d23bdf5b1b1b 4 years ago 643MB
root@iZwz9doa4kcnsjcv8fgt8pZ:/data/devops-demo#
5.5 启动容器
docker run -d -p 8001:8080 devopsdemo
-d参数是让容器后台运行
-p 是做端口映射,此时将服务器中的8001端口映射到容器中的8080端口(项目中端口默认是8080)。
注意:阿里云ECS要开放8001端口
5.6 验证
在浏览器中访问http://120.24.95.76:8001/devops/hello
,如果能访问到hello接口,表示Dcoker部署SpringBoot项目成功。
六、使用maven构建镜像
上边构建的过程是通过手工一步一步完成,maven提供docker-maven-plugin
插件可以完成从打包到构建镜像、构建容器等过程。
更多信息,请参考:docker-maven-plugin官方地址
6.1 编写pom_docker.xml
1、创建pom_docker.xml
文件
2、将项目中pom.xml
文件的内容全部复制到pom_docker.xml
文件中
3、在pom_docker.xml
文件中加入docker-maven-plugin
插件内容
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.2.2</version>
<!--docker 镜像相关的配置信息-->
<configuration>
<!--镜像名,这里用工程名:devops-->
<imageName>${project.artifactId}</imageName>
<!--<imageName>devopsdemo</imageName>-->
<!-- 指定 Dockerfile 路径-->
<dockerDirectory>${project.basedir}/src/main/resources</dockerDirectory>
<!--<dockerDirectory>${basedir}/docker</dockerDirectory>-->
<!--TAG,这里用工程版本号 -->
<imageTags>
<imageTag>${project.version}</imageTag>
</imageTags>
<!--构建镜像的配置信息 -->
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.artifactId}-${project.version}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
pom_docker.xml
文件全部内容如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.alanchen</groupId>
<artifactId>devops</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>devops</name>
<description>Demo project for DevOps</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.2.2</version>
<!--docker 镜像相关的配置信息-->
<configuration>
<!--镜像名,这里用工程名:devops-->
<imageName>${project.artifactId}</imageName>
<!--<imageName>devopsdemo</imageName>-->
<!-- 指定 Dockerfile 路径-->
<dockerDirectory>${project.basedir}/src/main/resources</dockerDirectory>
<!--<dockerDirectory>${basedir}/docker</dockerDirectory>-->
<!--TAG,这里用工程版本号 -->
<imageTags>
<imageTag>${project.version}</imageTag>
</imageTags>
<!--构建镜像的配置信息 -->
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.artifactId}-${project.version}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
</plugins>
</build>
</project>
6.2 编写Dockerfile文件
在工程的resources目录下新建Dockerfile文件,文件内容和上面的Dockerfile文件内容一样。
# 基础镜像使用java
FROM java:8
# 作者
MAINTAINER eangulee chenyan900520@126.com
# VOLUME 指定了临时文件目录为/tmp。
# 其效果是在主机 /var/lib/docker 目录下创建了一个临时文件,并链接到容器的/tmp
VOLUME /tmp
# 将jar包添加到容器中并更名为app.jar
ADD *.jar app.jar
# 运行jar包
RUN bash -c 'touch /app.jar'
#指定容器启动时要执行的命令,但如果存在CMD指令,CMD中的参数会被附加到ENTRYPOINT指令的后面
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
项目目录如下
6.3 提交代码
将本地项目devops编辑的代码全部都提交到远程GitLab仓库。
6.4连接登录阿里云ECS宿主机
AC-2:devops AC$ ssh root@120.24.95.76
# 按提示输入密码登录
6.5 拉取devops项目代码
1、进入目录
cd /data/devops-demo
2、clone代码
git clone http://120.24.95.76:6001/root/devops.git
root@iZwz96ew7wfkgebgbq9crbZ:/data/devops-demo# ls
devops
root@iZwz96ew7wfkgebgbq9crbZ:/data/devops-demo#
3、进入工程目录
cd devops
4、打包构建镜像
mvn -f pom_docker.xml clean package -DskipTests docker:build
打包过程会去下载依赖包,如果下载速度慢,可以将下载镜像地址改成国内地址。
成功构建结果如下:
---> Running in 0e76046ca6e7
Removing intermediate container 0e76046ca6e7
---> 6b505550ff52
Step 6/6 : ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
---> Running in 1bd9886f3c3a
Removing intermediate container 1bd9886f3c3a
---> ce9d7605919b
ProgressMessage{id=null, status=null, stream=null, error=null, progress=null, progressDetail=null}
Successfully built ce9d7605919b
Successfully tagged devops:latest
[INFO] Built devops
[INFO] Tagging devops with 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 04:38 min
[INFO] Finished at: 2022-01-16T16:37:09+08:00
[INFO] ------------------------------------------------------------------------
root@iZwz96ew7wfkgebgbq9crbZ:/data/devops-demo/devops#
5、制作完成后通过docker images命令查看我们制作的镜像
docker images
root@iZwz96ew7wfkgebgbq9crbZ:/data/devops-demo/devops# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
devops 0.0.1-SNAPSHOT ce9d7605919b 38 seconds ago 678MB
devops latest ce9d7605919b 38 seconds ago 678MB
gitlab/gitlab-ce latest 508bfaaaf273 4 days ago 2.36GB
registry latest b8604a3fe854 2 months ago 26.2MB
java 8 d23bdf5b1b1b 5 years ago 643MB
root@iZwz96ew7wfkgebgbq9crbZ:/data/devops-demo/devops#
5、创建启动容器
docker run -d -p 8002:8080 devops:0.0.1-SNAPSHOT
备注:阿里云ECS安全组要开放8002端口。
6、验证
在浏览器中访问http://120.24.95.76:8002/devops/hello
,如果能访问到hello接口,表示maven构建镜像启动容器成功。
七、ECS宿主机安装Jenkins
安装步骤可参考我另外一篇文章Ubuntu安装Jenkins
八、Jenkins持续集成
目标:通过Jenkins持续集成,git提交代码后,自动编译打包发布程序。
8.1 使用dockerfile-maven-plugin
插件
在上面介绍了使用Spotify公司开发的docker-maven-plugin
插件来构建Docker镜像。然而在如下所示官方申明中,Spotify官方已经不再推荐使用该插件:
上面说明了不再推荐使用该插件的原因,转而推荐了另外一款由该公司开发的Maven插件dockerfile-maven-plugin
。下面我们使用dockerfile-maven-plugin
插件来构建Docke镜像
8.1.1 编写pom_docker_registry.xml文件
在devops工程目录下编写pom_docker_registry.xml
文件。
1、创建pom_docker_registry
文件
2、将项目中pom_docker.xml
文件的内容全部复制到
pom_docker_registry.xml
文件中
3、在pom_docker_registry.xml
文件中更改plugins
标签内的内容
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.13</version>
<executions>
<execution>
<id>default</id>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
<!--docker 镜像相关的配置信息-->
<configuration>
<repository>120.24.95.76:5000/${project.artifactId}</repository>
<tag>${project.version}</tag>
<buildArgs>
<JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
</plugins></imageName>
pom_docker_registry.xml
文件全部内容如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.alanchen</groupId>
<artifactId>devops</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>devops</name>
<description>Demo project for DevOps</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.13</version>
<executions>
<execution>
<id>default</id>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
<!--docker 镜像相关的配置信息-->
<configuration>
<repository>120.24.95.76:5000/${project.artifactId}</repository>
<tag>${project.version}</tag>
<buildArgs>
<JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
可以看到,该插件的配置比docker-maven-plugin更简单了。repository指定docker镜像的repo名字。tag指定docker镜像的tag。buildArgs可以指定一个或多个变量,传递给Dockerfile,在Dockerfile中通过ARG指令进行引用。
另外,可以在execution中同时指定build和push目标。当运行mvn package时,会自动执行build目标,构建Docker镜像。当运行mvn deploy命令时,会自动执行push目标,将Docker镜像push到Docker仓库。
4、增加Dockerfile文件
在项目根目录(和pom文件在同一级)新建一个Dokerfile文件,文件内容如下:
# 基础镜像使用java
FROM java:8
# 作者
MAINTAINER eangulee chenyan900520@126.com
# VOLUME 指定了临时文件目录为/tmp。
# 其效果是在主机 /var/lib/docker 目录下创建了一个临时文件,并链接到容器的/tmp
VOLUME /tmp
# 将jar包添加到容器中并更名为app.jar
ARG JAR_FILE
ADD target/${JAR_FILE} /app.jar
# 运行jar包
RUN bash -c 'touch /app.jar'
#指定容器启动时要执行的命令,但如果存在CMD指令,CMD中的参数会被附加到ENTRYPOINT指令的后面
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
5、项目目录如下
6、提交代码
将本地项目devops编辑的代码全部都提交到远程GitLab仓库。
8.1.2 修改maven setting.xml配置
先找到配置文件settings.xml位置,通过mvn -v可以显示maven的安装目录,在安装目录下就有settings.xml配置文件。在pluginGroups中增加一个com.spotify
vi /usr/share/maven/conf/settings.xml
8.2 Jenkins集成
8.2.1 Jenkins创建持续集成任务
填入shell脚本内容
#!/bin/bash
result=$(docker ps | grep "120.24.95.76:5000/devops")
# -n 表示:result字符串长度大于0,注意[[ ]]内部两边有空格
if [[ -n "$result" ]]
then
echo "stop devops"
docker stop devops
fi
result1=$(docker ps -a | grep "120.24.95.76:5000/devops")
if [[ -n "$result1" ]]
then
echo "rm devops"
docker rm devops
fi
result2=$(docker images | grep "1120.24.95.76:5000/devops")
if [[ -n "$result2" ]]
then
echo "120.24.95.76:5000/devops:0.0.1-SNAPSHOT"
docker rmi 120.24.95.76:5000/devops:0.0.1-SNAPSHOT
fi
执行maven构建
clean package -f pom_docker_registry.xml -DskipTests dockerfile:build
拉取镜像,创建容器,启动容器
shell命令
docker run --name devops -p 8002:8080 -idt 120.24.95.76:5000/devops:0.0.1-SNAPSHOT
配置完了,点击保存
点击保存按钮后的界面
8.2.2 手动构建
8.2.3 构建成功
1、显示构建成功
2、访问docker私有仓库http://120.24.95.76:5000/v2/_catalog
,显示了devops
8.2.4 构建失败情况
8.2.4.1 构建失败情况一
问题描述:在shell脚本运行docker报权限问题,错误信息如下
Got permission denied while trying to connect to the Docker daemon socket at unix
解决方法:将Jenkins用户加入docker组,重启Jenkins服务
sudo gpasswd -a jenkins docker
sudo service jenkins restart
8.2.4.2 构建失败情况二
问题描述:镜像推送时出现 server gave HTTP response to HTTPS client 问题
[�[1;31mERROR�[m] Failed to execute goal �[32mcom.spotify:docker-maven-plugin:1.2.2:build�[m �[1m(default-cli)�[m on project
[36mdevops�[m: �[1;31mException caught�[m: Get "https://120.24.95.76:5000/v2/": http: server gave HTTP response to HTTPS client
原因分析:因为 Docker引擎默认通过 https 协议与 Docker Registry 通信,所以如果搭建的Docker 私有镜像库是 http 协议的话,就会输出上述日志。
解决方法:
情况1.这种写法是没有配置Docker加速器的情况下,在 /etc/docker/daemon.json
中设置以下
// 没有配置加速器的
// 单个私服的写法
{
"insecure-registries": ["registry的IP地址:端口号"]
}
// 多个私服的写法
{
"insecure-registries": ["registry1的IP地址:端口号","registry2的IP地址:端口号"]
}
情况2.这种写法是配置过Docker加速器的情况下,在 /etc/docker/daemon.json
中设置以下:
// 没有配置加速器的
// 单个私服的写法
{
"registry-mirrors": ["http://f1361db2.m.daocloud.io"],
"insecure-registries": ["registry的IP地址:端口号"]
}
// 多个私服的写法
{
"registry-mirrors": ["http://f1361db2.m.daocloud.io"],
"insecure-registries": ["registry1的IP地址:端口号","registry2的IP地址:端口号"]
}
我的配置为:
{"insecure-registries":["120.24.95.76:5000"]}
以上配置完成以后使用命令
systemctl daemon-reload
systemctl restart docker.service
systemctl enable docker.service
注意:执行完上面命令后,需要去重新启动gitlab、docker私有仓库服务。
8.2.4.3 构建失败情况三
构建失败错误信息
docker: Error response from daemon: Conflict. The container name "/devops" is already in use by container "30ae08c83d81b79d8688cf67affb1edd1b742e2ad1eff513d5be2a81c90a6532". You have to remove (or rename) that container to be able to reuse that name.
查看镜像和容器情况,可以看出在重新构建后,之前的那个容器没有被停掉,导致再启动容器时,启动失败
问题原因:
在Jenkins里的shell脚本有问题,导致脚本执行失败,重新构建时没有删除旧的容器和镜像。比如,在[[ ]]]内两边没有留空格,导致shell脚本错误。
错误的写法:
if [[-n "$result"]]
正确的写法
# -n 表示:result字符串长度大于0,注意[[ ]]内部两边有空格
if [[ -n "$result" ]]
为了避免shell脚本错误导致执行失败,我们可以在服务器目录下,新建一个shell脚本文件,然后执行测试,看是否能正常执行,比如
vi test.sh
内容为将要在Jenkins要执行的shell脚本
#!/bin/bash
result=$(docker ps | grep "120.24.95.76:5000/devops")
# -n 表示:result字符串长度大于0,注意[[ ]]内部两边有空格
if [[ -n "$result" ]]
then
echo "stop devops"
docker stop devops
fi
result1=$(docker ps -a | grep "120.24.95.76:5000/devops")
if [[ -n "$result1" ]]
then
echo "rm devops"
docker rm devops
fi
result2=$(docker images | grep "1120.24.95.76:5000/devops")
if [[ -n "$result2" ]]
then
echo "120.24.95.76:5000/devops:0.0.1-SNAPSHOT"
docker rmi 120.24.95.76:5000/devops:0.0.1-SNAPSHOT
fi
执行test.sh脚本
sudo bash ./test.sh
执行过程
root@iZwz9f4eh9lmah6qiq41zvZ:~# sudo bash ./test.sh
120.24.95.76:5000/devops:0.0.1-SNAPSHOT
Untagged: 120.24.95.76:5000/devops:0.0.1-SNAPSHOT
Deleted: sha256:48288cce36ea8c2eead8e3a58dc0890b7a3c2edf3ab95a77fd75cc140a7d73f8
Deleted: sha256:3a9b395bca1c89fb12aedfb0dab365b212dffdc775ef1a646acc7d80fd072d86
Deleted: sha256:b3e98485e2b5e7b8c7a89c64cdf44039de5ff796152ce800e906f20d2756ca33
Deleted: sha256:48b6b69723281b1c4bc221bcda5ff0b4db1175a3db2a97a47c6ef4a4624c27d9
Deleted: sha256:19b3570f0e1341cb71f3b35b307a5aa088fac708663965ca213cecde0c89164b
root@iZwz9f4eh9lmah6qiq41zvZ:~#
8.2.4.4 构建失败情况四
错误信息
Must specify baseImage if dockerDirectory is null
问题原因:
JenkinsInvoke top-level Maven targets
错误地设置成docker:build
,dockerDirectory
是插件docker-maven-plugin
的配置,所以报错。
解决方案:
检查Jenkins Invoke top-level Maven targets
设置,因为我们使用的是dockerfile-maven-plugin
,因此命令是dockerfile:build
而不是docker:build
clean package -f pom_docker_registry.xml -DskipTests dockerfile:build
8.3 Jenkins自动构建
8.3.1 Jenkins中拿到钩子地址
1、Jenkins安装Gitlab插件前Build Triggers的选项
2、安装Gitlab插件
安装完插件后要重启Jenkins插件才会生效
勾选GitLab webhook选项,点击保存,并复制URL地址http://120.79.114.234:8080/project/devops
,该地址需要配置到GitLab设置里
3、GitLab authentication设置
由于我们Jenkins访问GitLab用的是账号名密码,不是用的ssh keys,因此我们要取消authentication的勾选选项,否则使用钩子时会报403错误,如下
取消authentication的勾选选项操作如下:
8.3.2 GitLab配置webhook(钩子)
1、登录GitLab,在Network里找到这两个选项并进行勾选,点击保存。
2、进入devops
项目,点击Settings-Webhooks,粘贴Jenkins那边得到的URL地址,点击【Add webhook】按钮进行保存。
3、在Test里选择Push events进行Push测试。
8.3.3 修改代码push后自动构建
1、将hello方法里的代码改成Kyra
@RestController
@RequestMapping("devops")
public class DevOpsController {
@GetMapping("hello")
public String hello(){
return "Kyra";
}
}
2、git提交代码
AC-2:devops AC$ git add .
AC-2:devops AC$ git commit -m "Kyra"
[main 094c6d7] Kyra
1 file changed, 1 insertion(+), 1 deletion(-)
AC-2:devops AC$ git push
Counting objects: 10, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (10/10), 719 bytes | 719.00 KiB/s, done.
Total 10 (delta 2), reused 0 (delta 0)
To http://120.79.114.234:6001/root/devops.git
3eec4b3..094c6d7 main -> main
AC-2:devops AC$
3、git push后Jenkins会自动构建
4、刷新接口,接口返回修改后的Kyra
http://120.24.95.76:8002/devops/hello