1. 前言
随着项目上容器技术的广泛应用,我也加入了Docker容器技术的学习。首先初学Docker,我的想法很简单。创建一个SpringBoot项目,如何将SpringBoot项目打包成容器镜像,然后推送至远程的Docker服务上部署。带着这个目的查阅了一些资料后,整体的实现思路如下:
- 构建一个Docker的私有镜像仓库,用于存储Docker镜像,当需要启动容器服务时可以指定Docker去私有镜像仓库获取镜像;
- SpringBoot在打包的时候,使用插件对镜像进行打包并推送到远程的Docker镜像仓库;
- Docker启动容器时指定获取镜像的仓库。
环境描述:
准备一台阿里云服务器,已经完成Docker服务安装,后续将会在该服务器上进行搭建Docker私有镜像仓库。本地准备一个SpringBoot项目,至少编写一个Controller,保证本地SpringBoot项目启动后能够正常访问到Contriller。
2. 搭建建Docker私有镜像仓库
- 修改配置
registry-mirrors: 配置的是阿里云镜像加速地址,此项配置不是搭建私有仓库必须的。
insecure-registries: 此项配置是让Docker信任私有仓库地址,xx.96.194.xx为服务器的IP,2112为私有仓库的端口。
# vim /etc/docker/daemon.json
{
"registry-mirrors": ["https://xxxxxxxx.mirror.aliyuncs.com"],
"insecure-registries": ["xx.96.194.xx:2112"]
}
- 拉取私有仓库镜像
docker pull registry
- 重新加载配置信息,重启Docker服务
# 重新加载某个服务的配置文件
sudo systemctl daemon-reload
# 重新启动 docker
sudo systemctl restart docker
3. 创建私有仓库并配置认证
由于私有镜像仓库是部署在阿里云上,要确保私有仓库的安全性,需要一个安全认证证书,防止发生意想不到的事情。所有需要在搭建私有仓库的Docker主机上先生成自签名证书。
- 创建证书存储目录
mkdir -p /usr/local/registry/certs
- 生成自签名证书命令
openssl req -newkey rsa:2048 -nodes -sha256 -keyout /usr/local/registry/certs/domain.key -x509 -days 365 -out /usr/local/registry/certs/domain.crt
- openssl req:创建证书签名请求等功能;
- -newkey: 创建 CSR 证书签名文件和 RSA 私钥文件;
- rsa:2048:指定创建的 RSA 私钥长度为 2048;
- -nodes:对私钥不进行加密;
- -sha256:使用 SHA256 算法;
- -keyout:创建的私钥文件名称及位置;
- -x509:自签发证书格式;
- -days:证书有效期;
- -out:指定 CSR 输出文件名称及位置;
生成自签名证书:
通过openssl命令先生成自签名证书,运行命令后需要填写一些证书信息。其中Common Name填写的xx.96.104.xxx是最关键的信息,这里填写的是私有仓库的地址:
Country Name:国家
State or Province Name:州或省
Locality Name:城市
Organization Name :机构名称
Organizational Unit Name :组织单位名称
Common Name:hostname域名
Email Address:邮箱地址
[root@iZ2ze0r5hel5o6fik77a26Z ~]# openssl req -newkey rsa:2048 -nodes -sha256 -keyout /usr/local/registry/certs/domain.key -x509 -days 365 -out /usr/local/registry/certs/domain.crt
Generating a 2048 bit RSA private key
..............+++
............................+++
writing new private key to '/usr/local/registry/certs/domain.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:BJ
Locality Name (eg, city) [Default City]:BJ
Organization Name (eg, company) [Default Company Ltd]:docker
Organizational Unit Name (eg, section) []:docker
Common Name (eg, your name or your server's hostname) []:xx.96.104.xxx
Email Address []:xxxxx@163.com
- 生成鉴权密码文件
htpasswd 是 apache http 的基本认证文件,使用 htpasswd 命令可以生成用户及密码文件:
# 创建存储鉴权密码文件目录
mkdir -p /usr/local/registry/auth
# 如果没有 htpasswd 功能需要安装 httpd
yum install -y httpd
# 创建用户和密码
htpasswd -Bbn root ***1234 > /usr/local/registry/auth/htpasswd
- 创建私有仓库容器
- -d:后台运行容器;
- --name:为创建的容器命名;
- -p:表示端口映射,前者是宿主机端口,后者是容器内的映射端口。可以使用多个 -p 做多个端口映射;
- -v:将容器内 /var/lib/registry 目录下的数据挂载至宿主机 /mydata/docker_registry 目录下;
docker run -di --name registry -p 2112:5000 \
-v /mydata/docker_registry:/var/lib/registry \
-v /usr/local/registry/certs:/certs \
-v /usr/local/registry/auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
registry
4. 操作私有容器镜像仓库
- 首先通过 docker login 命令输入账号密码登录私有仓库
[root@iz2zeh3bjq6rgoli6ng5g9z ~]# docker login xx.96.194.xx:2112
Username: root
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
- 推送镜像至私有仓库
先给镜像设置标签 docker tag local-image:tagname new-repo:tagname
再将镜像推送至私有仓库 docker push new-repo:tagname
[root@iz2zeh3bjq6rgoli6ng5g9z ~]# docker tag hello-world:latest xx.96.194.xx:2112/test-hello-world:1.0.0
[root@iz2zeh3bjq6rgoli6ng5g9z ~]# docker push xx.96.194.xx/test-hello-world:1.0.0
The push refers to repository [xx.96.194.xx/test-hello-world]
An image does not exist locally with the tag: xx.96.194.xx/test-hello-world
[root@iz2zeh3bjq6rgoli6ng5g9z ~]#
- 查看推送至私有仓库的镜像
由于我们做了目录挂载,因此可以在宿主机 /mydata/docker_registry/docker/registry/v2/repositories 目录下查看。
[root@iz2zeh3bjq6rgoli6ng5g9z ~]# ll /mydata/docker_registry/docker/registry/v2/repositories/
total 8
drwxr-xr-x 5 root root 4096 Dec 15 10:04 actual-combat
drwxr-xr-x 5 root root 4096 Dec 14 12:36 test-hello-world
[root@iz2zeh3bjq6rgoli6ng5g9z ~]#
- 退出docker账号
所有的推送镜像操作完成后,可以使用docker logout 命令退出账号。
[root@iz2zeh3bjq6rgoli6ng5g9z ~]# docker logout xx.96.194.xx
Removing login credentials for xx.96.194.xx
[root@iz2zeh3bjq6rgoli6ng5g9z ~]#
5.搭建SpringBoot工程
- 创建一个Controller
@Slf4j
@RestController
@RequestMapping(value = "user")
public class UserController {
@RequestMapping(value = "/info")
@ResponseBody
public String getUser() {
User user = new User();
user.setUserName("张三");
user.setPassword("qwertyuiop");
return JSONObject.toJSONString(user);
}
}
-
启动SpringBoot工程保证Controller能够正常访问
6. SpringBoot项目集成镜像打包插件
- pom文件引入jib-maven-plugin插件依赖
<dependency>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>2.8.0</version>
</dependency>
- pom文件编写jib镜像打包plugin
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>2.8.0</version>
<configuration>
<!-- 拉取所需的基础镜像 -->
<from>
<!-- 默认从官方公共仓库拉取镜像,速度较慢 -->
<image>openjdk:alpine</image>
<!-- 从指定仓库拉取镜像提速(需提前将镜像 push 至仓库) -->
<!--<image>192.168.10.10:5000/openjdk:alpine</image>-->
</from>
<!-- push 到哪个镜像仓库(公共仓库、阿里云仓库、私有自建仓库等) -->
<to>
<!-- 使用 DockerHub 的官方公共仓库:仓库地址/用户名/镜像名 -->
<!--<image>registry.hub.docker.com/mrhelloworld/${project.name}</image>-->
<!-- 使用自建的私有仓库:仓库地址/镜像名 -->
<image>xx.96.194.xx:2112/${project.artifactId}</image>
<!-- 镜像版本号 -->
<tags>
<tag>${project.version}</tag>
</tags>
<!-- 连接仓库的账号密码 -->
<auth>
<username>root</username>
<password>xxxxxx</password>
</auth>
</to>
<!-- 使 jib 插件支持 http 协议连接镜像仓库(安全起见,默认是关闭的) -->
<allowInsecureRegistries>true</allowInsecureRegistries>
<container>
<!-- 启动类 -->
<mainClass>com.erxiao.actualCombat.ActualCombatApplication</mainClass>
</container>
</configuration>
</plugin>
- 打开Terminal执行打包命令
jib:build命令会完成镜像打包,并推送到远程仓库。
mvn install -Dmaven.test.skip=true
mvn compile jib:build -Dmaven.test.skip=true
7. 查看并启动镜像
- 查看镜像
查看镜像有两个方式,方式一,使用curl命令连接docker私有仓库查看。方式二,安装registry-web通过Web UI界面查看。
- 使用curl命令
curl -kiv -H "Authorization: Basic $(echo -n "root:wangxxxx" | base64)" https://localhost:2112/v2/_catalog 其中root:wangxxxx为关键信息,对应着生成鉴权密码文件时,用htpasswd 命令生成的用户与密码,用户名与密码直接使用英文冒号分隔。返回的{"repositories":["actual-combat","test-hello-world"]}内容就是当前私有仓库中的镜像。
# curl -kiv -H "Authorization: Basic $(echo -n "root:wangxxxx" | base64)" https://localhost:2112/v2/_catalog
* About to connect() to localhost port 2112 (#0)
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 2112 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* skipping SSL peer certificate verification
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* subject: E=xxxxxxxx_email@sina.com,CN=xx.96.194.xxx,OU=erxiao,O=erxiao,L=BJ,ST=BJ,C=CN
* start date: Dec 14 04:42:52 2021 GMT
* expire date: Dec 14 04:42:52 2022 GMT
* common name: xx.96.194.xxx
* issuer: E=xxxxxx_email@sina.com,CN=xx.96.194.xxx,OU=erxiao,O=erxiao,L=BJ,ST=BJ,C=CN
> GET /v2/_catalog HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:2112
> Accept: */*
> Authorization: Basic cm9vdDp3YW5nY29uZw==
>
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
Content-Type: application/json; charset=utf-8
< Docker-Distribution-Api-Version: registry/2.0
Docker-Distribution-Api-Version: registry/2.0
< X-Content-Type-Options: nosniff
X-Content-Type-Options: nosniff
< Date: Sat, 18 Dec 2021 12:53:12 GMT
Date: Sat, 18 Dec 2021 12:53:12 GMT
< Content-Length: 54
Content-Length: 54
<
{"repositories":["actual-combat","test-hello-world"]}
* Connection #0 to host localhost left intact
- 安装registry-web通过页面查看镜像
参数解释:
-p 7001:8080 host主机端口8080映射到container8080
--name 起个名字,docker logs start stop时就可以用了
--link 连接registry,可以不加,以为下面有registry的url访问方式(registry必须和与私有镜像仓库保持一致)
-e REGISTRY_TRUST_ANY_SSL=true, 环境变量,相信所有的ssl,因为我们大部分的ssl都是自建的
-e REGISTRY_BASEIC_AUTH 这个里面的值是账号以及密码的base64获取的,可以通过
echo "ops:123123123" | base64
b3BzOjEyMzEyMzEyMwo=
获得。
-e REGISTRY_NAME, 目前看起来这个是会在页面上的显示
docker run -itd -p 7001:8080 --name registry-web --link registry \
-e REGISTRY_URL=https://xx.96.194.xxx:2112/v2 \
-e REGISTRY_TRUST_ANY_SSL=true \
-e REGISTRY_BASIC_AUTH="cm9vdDp3YW5nY29uZw==" \
-e REGISTRY_NAME=docker_registry hyper/docker-registry-web
页面访问:
页面访问:http://xx.96.194.xxx:7001/
- 启动镜像
docker run -di --name actual-combat -p 7000:8080 xx.96.194.xxx:2112/actual-combat:latest
--name指定镜像名称
actual-combat:latest需要和私有镜像仓库中的镜像保持一致
[root@iz2zeh3bjq6rgoli6ng5g9z ~]# docker run -di --name actual-combat -p 7000:8080 xx.96.194.xxx:2112/actual-combat:latest
12f8cf18723fed54e17702659929e538cf8a878802a0820eeb46996962543e61
[root@iz2zeh3bjq6rgoli6ng5g9z ~]#
页面访问Spring Boot项目
8. 常用命令
- 获取当前运行的镜像
docker ps
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
12f8cf18723f xx.96.194.xxx:2112/actual-combat:latest "java -cp /app/resou…" 17 minutes ago Up 17 minutes 0.0.0.0:7000->8080/tcp actual-combat
59d00326d2b7 hyper/docker-registry-web "start.sh" About an hour ago Up About an hour 0.0.0.0:7001->8080/tcp registry-web
4c0fd415f702 registry "/entrypoint.sh /etc…" 4 days ago Up 4 days 0.0.0.0:2112->5000/tcp registry
- 停止镜像
docker stop [CONTAINER ID]
[root@iz2zeh3bjq6rgoli6ng5g9z ~]# docker stop 8de8c650c485
8de8c650c485
[root@iz2zeh3bjq6rgoli6ng5g9z ~]#
- 删除镜像
docker rm [CONTAINER ID]
[root@iz2zeh3bjq6rgoli6ng5g9z ~]# docker rm 8de8c650c485
8de8c650c485
[root@iz2zeh3bjq6rgoli6ng5g9z ~]#
- 查看容器日志
docker logs -f [CONTAINER ID]
[root@iz2zeh3bjq6rgoli6ng5g9z ~]# docker logs -f 59d00326d2b7
CATALINA_OPTS: -Djava.security.egd=file:/dev/./urandom -Dcontext.path=
Using CATALINA_BASE: /var/lib/tomcat7
Using CATALINA_HOME: /usr/share/tomcat7
Using CATALINA_TMPDIR: /var/lib/tomcat7/temp
Using JRE_HOME: /usr/lib/jvm/java-7-openjdk-amd64
Using CLASSPATH: /usr/share/tomcat7/bin/bootstrap.jar:/usr/share/tomcat7/bin/tomcat-juli.jar
Dec 18, 2021 1:02:07 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8080"]
参考:Docker 私有镜像仓库的搭建及认证
参考:Spring Boot 多样化构建 Docker 镜像
参考:registry-web集成安全认证