Docker实践学习笔记

1 PostgreSQL和插件的部署

需求:使用docker制作PostgreSQL的镜像并部署,同时需要为其安装postgis,pgrouting和timescaledb插件。

  1. 拉取基础镜像postgis/postgis,该镜像包含了postgrespostgis
1
docker pull postgis/postgis
  1. 编写dockerfile:这里以PostgresSQL 16为例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
FROM postgis/postgis:latest

# 设置腾讯云镜像源
RUN sed -i 's|http://deb.debian.org/debian|http://mirrors.tencentyun.com/debian|g' /etc/apt/sources.list

# 下载wget
RUN apt-get update && apt-get install wget

# 获取timescaledb的秘钥
RUN wget --quiet -O - https://packagecloud.io/timescale/timescaledb/gpgkey | apt-key add -

# 增加timescaledb的源
RUN sh -c "echo 'deb https://packagecloud.io/timescale/timescaledb/ubuntu/ focal main' > /etc/apt/sources.list.d/timescaledb.list"

# 安装timescaledb
RUN apt-get update && \
apt-get install -y timescaledb-2-2.16.1-postgresql-16

# 安装 pgRouting
RUN apt-get update && \
apt-get install -y postgresql-$PG_MAJOR-pgrouting

# 确保所有初始化脚本都被执行
CMD ["postgres"]

说明:

1
2
# 换源
sed -i 's|http://deb.debian.org/debian|http://mirrors.tencentyun.com/debian|g' /etc/apt/sources.list

^7c1a05

  • -i 表示直接在原文件中进行修改,而不是将结果输出到标准输出。
  • 's|old|new|g'
    • ssed 的替换命令,表示将一个字符串替换成另一个字符串。
    • | 是定界符,通常用 / 作为定界符,但在这里使用了 |,这在 URL 替换中很常见,因为 URL 中包含 /,这样可以避免冲突。
    • g 代表全局替换,表示替换行内所有出现的匹配字符串。如果省略 g,只会替换每行中第一个匹配的字符串。
  • 最后一个参数是sed操作的文件

1
sh -c "echo 'deb https://packagecloud.io/timescale/timescaledb/ubuntu/ focal main' > /etc/apt/sources.list.d/timescaledb.list"

^18d0f4

  • sh -c 是一个 shell 命令,用于执行字符串形式的命令。-c 表示后面跟随的内容是一条完整的命令,传递给 sh 来执行。

1
wget --quiet -O - https://packagecloud.io/timescale/timescaledb/gpgkey | apt-key add -

这条命令用于下载 TimescaleDB 的 GPG 公钥并将其添加到系统的受信任密钥列表中,以便 APT 包管理器能够验证从 TimescaleDB 软件源下载的软件包的真实性和完整性。

  • wget --quiet -O - https://packagecloud.io/timescale/timescaledb/gpgkey:
    • wget: 是一个用于从网络上下载文件的命令行工具。
    • --quiet: 这个选项使 wget 以静默模式运行,不输出下载过程中的信息,仅输出错误和重要消息。
    • -O -: 选项 -O 指定将下载的内容输出到某个文件,而不是默认的文件名。- 表示输出到标准输出(通常是终端)。
  • https://packagecloud.io/timescale/timescaledb/gpgkey: 这是 TimescaleDB 的 GPG 公钥的 URL。wget 从这个地址下载公钥文件。
  • apt-key add -:
    • apt-key: 是一个 APT 包管理工具,用于管理 APT 的受信任密钥列表。
    • add: 是 apt-key 的一个子命令,用于添加新的密钥到系统的受信任密钥列表中。
    • -: 指定从标准输入(即管道)读取密钥数据。apt-key add - 将通过管道传递过来的 GPG 公钥添加到 APT 的受信任密钥列表中。
  1. 构建镜像:
1
docker build -t postgres:tag .
  1. 创建数据卷,并运行容器:
1
2
3
4
5
6
7
8
9
10
11
docker volume create pg_data

docker run -d \
--name postgres \
-e POSTGRES_USER=admin \ # 用户名
-e POSTGRES_PASSWORD=admin123 \ # 密码
-e POSTGRES_DB=airport \ # 数据库
-e TZ=Asia/Shanghai \
-p 5432:5432 \
-v pg_data:/var/lib/postgresql/data \ # 挂载到pg_data
postgres:tag

其中数据卷pg_data保存postgresql的持久化数据。注意,如果使用目录挂载,数据库、用户和密码设置不会生效。

  1. 加载插件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 进入postgres容器
docker exec -it postgres /bin/bash

# 预加载timescaledb
echo "shared_preload_libraries = 'timescaledb'" >> /var/lib/postgresql/data/postgresql.conf

# 这里需要重启容器!!!
docker restart postgres

#--------------------------------------------------------

# 再次进入postgres容器
docker exec -it postgres /bin/bash

# 连接数据库
psql -U admin -d airport

# 扩展插件
create extension pgrouting;
create extension timescaledb;

# 验证
select extname from pg_extension;

2 多模块系统部署

描述:现在有一个多模块的系统,即父模块(只有pom管理依赖)下有多个子模块,程序的入口位于子模块admin中。现在需要打成jar包,并使用docker部署在服务器中。

  • 入口模块adminpom添加:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>xxx</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<finalName>xxxx</finalName>
</build>

这里的finalName为打包后jar包的名称。

  • admin目录下执行下列命令,生成可执行jaradmin.jar
1
mvn clean package
  • 准备dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 基础镜像
FROM openjdk:8-jre

WORKDIR /app

# 设定时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# 拷贝jar包
COPY admin.jar /admin.jar

EXPOSE 8080

# 入口
ENTRYPOINT ["java", "-jar", "admin.jar"]
  • Dockerfilejar包上传至服务器的文件夹中,例如/usr/local/admin
1
2
3
docker build -t admin:tag .

docker run -d --name admin -p 8080:8080 admin:tag

3 容器的日志

容器的日志,可以在容器中查看,也可以将日志文件的目录挂载到宿主机的目录上(或数据卷),直接在宿主机上查看。

假设logback的配置文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 日志存放路径 -->
<property name="log.path" value="/usr/local/xxx/logs"/>
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />

<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>

<!-- 文件输出 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys_info.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys_info.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>30</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>

<!-- 日志级别 -->
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>

</configuration>

其中,日志文件的输出位置位于/usr/local/xxx/logs

  • 如果Dockerfile没有另外设置WORKDIR,则日志位于容器的上述目录,可进入容器中查看:
1
2
3
4
5
6
7
8
9
docker exec -it <container_id> /bin/bash

# 进入容器

cd /usr/local/xxx/logs

# 即可查看日志文件sys_info.log

cat sys_info.log

如果设置了WORKDIR,假设为app,则:

1
2
3
4
5
6
7
8
9
docker exec -it <container_id> /bin/bash

# 进入容器

cd app/usr/local/xxx/logs

# 即可查看日志文件sys_info.log

cat sys_info.log
  • 可以将该目录挂载到宿主机的某个目录中:
1
docker run -d --name xxxx -v /usr/local/xxx/logs:/usr/local/xxx/logs xxx:latest

则可以在宿主机中直接查看日志:

1
2
3
cd /usr/local/xxx/logs

cat sys_info.log

同样的,如果设置了WORKDIR,假设为app,则:

1
2
3
4
5
docker run -d --name xxxx -v /usr/local/xxx/logs:app/usr/local/xxx/logs xxx:latest

cd /usr/local/xxx/logs

cat sys_info.log

4 离线安装Docker

  1. 下载 Docker 安装包:Docker 官方网站下载适合 Docker 安装包(中科大镜像源

一般来说,需要下载以下文件:

1
2
3
4
containerd.io_<version>_<arch>.deb
docker-ce_<version>_<arch>.deb
docker-ce-cli_<version>_<arch>.deb
docker-compose-plugin_<version>_<arch>.deb

其中arch的查看方法详见:[[处理器架构#1 查看]]

  • 安装
1
dpkg -i # 上述所有的deb,可能会报错,解决方法详见下方
  • 启动
1
2
3
4
5
systemctl start docker

systemctl enable docker

systemctl status docker
  • 验证
1
docker --version # 或者 docker version
  1. Docker Compose 发布页面 下载 Docker Compose 二进制文件。
  • 安装
1
cp docker-compose-<version> /usr/local/bin/docker-compose
  • 赋予执行权限
1
chmod +x /usr/local/bin/docker-compose
  • 验证
1
docker-compose --version

在安装Docker的过程中,可能会出现错误:

1
groupadd:无法打开 /etc/group

该错误的原因是因为对系统的关键文件进行了锁定,防止篡改,可以通过以下命令查看是否锁定:

1
lsattr /etc/group

如果显示:

1
----i---------- /etc/group

权限 i 表示该文件不能被删除、改名、设定链接关系,同时不能写入或新增内容。

可执行下列执行暂时放开权限:

1
2
chattr -i /etc/group
chattr -i /etc/gshadow

安装完成后,可再执行以下命令恢复配置:

1
2
chattr +i /etc/group
chattr +i /etc/gshadow

5 应用更新

问题描述:Docker部署springboot应用时,怎么设置才能避免每次更新jar包时,需要重新构建镜像?

  • 提前构建一份镜像
1
2
3
4
5
6
7
8
9
# Dockerfile
FROM openjdk:8-jre

WORKDIR var/lib/app/

EXPOSE 8082

# 这里不复制 JAR 文件,而是通过挂载
CMD ["java", "-jar", "var/lib/app/application.jar"]

构建镜像:

1
docker build -t application .
  • 使用docker-compose部署多个docker容器,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
services:
sgis-postgis:
image: postgres-timescale:15
container_name: postgis
volumes:
- "../postgres/data:/var/lib/postgresql/data/"
ports:
- "5432:5432"
networks:
- sgis_network
restart: always

rabbitmq:
image: rabbitmq:latest
container_name: rabbitmq
ports:
- "5672:5672"
- "15672:15672"
volumes:
- "../rabbitmq:/var/lib/rabbitmq" # 持久化RabbitMQ数据
environment:
- RABBITMQ_DEFAULT_USER: "fisher" # 设置默认用户名
- RABBITMQ_DEFAULT_PASS: "fisher" # 设置默认密码
networks:
- sgis_network
restart: always

redis:
image: redis:latest
container_name: redis
ports:
- "6379:6379"
networks:
- sgis_network
command:
--save 60 1
--loglevel warning
restart: always

tomcat:
image: "tomcat:latest"
container_name: tomcat
privileged: true
environment:
- TZ="Asia/Shanghai"
ports:
- "9016:8080"
networks:
- sgis_network
volumes:
- "../tomcat/webapps:/usr/local/tomcat/webapps"
restart: always

application:
image: application:latest
container_name: application
ports:
- "8082:8082"
volumes:
- "../app/application.jar:/var/lib/app/application.jar"
networks:
- sgis_network
restart: always
depends_on:
- sgis-postgis
- rabbitmq
- redis
networks:
sgis_network:
driver: bridge

其中../app/application.jar为本地的jar文件存放位置,/var/lib/app/application.jar为容器内jar的存放位置

使用命令启动多个容器:

1
docker-compose up -d

当jar更新时,首先停止并删除原来的application容器,然后替换原有的jar包,最后重启:

1
2
3
4
# 删除原来的容器
docker rm -f application

# 替换原有的jar包
1
2
# 重启
docker-compose up -d