目录
- 0 背景
- 1 安装和配置Docker
- 2 配置服务环境
- 2.1 安装MySQL
- 2.2 安装Redis
- 3 部署服务
- 4 总结
0 背景
你的多个服务都部署在了虚拟电脑上,并且它们运行得好好的,没有任何问题。现在你的计算机的状态是:一个Windows
操作系统,Windows
操作系统上安装了VirtualBox
或者VMware
之类的虚拟机软件,在虚拟机软件的帮助下,你安装了多台虚拟电脑,有些虚拟电脑上你只安装了数据库等组件,有些虚拟电脑上部署了你的微服务,这些虚拟电脑组合在一起形成了你的整个服务。
当你的服务运行了一段时间之后,你发现好像哪里不对劲。有的虚拟电脑上的服务或组件被频繁地使用,不过也不是什么坏事,这正是你需要的。但是有的虚拟电脑上的服务却被访问得很少,甚至几乎没被访问过。
这时你又开始思考,似乎用一个虚拟机给那些不被经常访问的服务来用有点浪费资源了,有没有更经济一点的方法呢?不对,被不被经常访问好像并不是你看不惯的原因,你看不惯的应该是这些虚拟机占用你的资源而没有被充分利用。
你终于明白了是什么地方不对劲了:这一个虚拟机上只部署一个服务太浪费资源了,一个虚拟电脑在安装时需要让你指定资源(内存,CPU,磁盘等),你最少得指定操作系统占用的空间,而一个操作系统要占几个G的磁盘,你的服务只是一个简单的服务,运行时峰值也就几百兆,而你需要使用到的操作系统的功能也不多,你就会想,怎么才能将这些用不到的功能去掉啊?
另外,纵观整个服务,它们都运行在Linux
系统上,尽管这些Linux
系统发行版不同,但是它们的内核是一样的。如果有一种“提取公因式”的技术,它能让所有这些虚拟机内核都集中在一起,然后将提取了“公因式”后的服务或组件成为一种“插件”,当把“插件”插入有内核的系统上,它就可以工作,那就好了。
还真有,于是你开始做接下来的事。
1 安装和配置Docker
同样,一个脚本搞定
#!/bin/bash
sudo apt update
sudo apt upgrade -y
# 安装必要工具
sudo apt install apt-transport-https
# 配置apt下载源
# 使用阿里源
aliyun_src="https://mirrors.aliyun.com/docker-ce/linux/ubuntu/"
# curl是自带的,没必要安装
curl -fsSL "${aliyun_src}gpg" | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] ${aliyun_src} $(. /etc/os-release && echo "${VERSION_CODENAME}") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 刷新源,这一步很重要
sudo apt update
# 通过apt安装docker
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 添加镜像源
# 创建目录
sudo mkdir -p /etc/docker# 写入配置文件
sudo tee /etc/docker/daemon.json <<-'EOF'
{"registry-mirrors": ["https://hub.crdz.gq","https://hub.littlediary.cn","https://docker.1panel.live","https://hub.xdark.top","https://docker.m.daocloud.io","https://docker.kejilion.pro","https://registry.dockermirror.com","https://docker.unsee.tech"]
}
EOF# 配置不加sudo使用docker
sudo usermod -aG docker "${USER}"
# 重启docker服务
sudo systemctl daemon-reload && sudo systemctl restart docker
2 配置服务环境
使用Docker
安装组件,就比在操作系统上直接安装要方便多了,而且又便于管理。下面仍然是以JDK
,MySQL
和Redis
举例,实际上在Docker
中,无论你想安装什么都是一样的命令,只需要给个组件名和版本号即可(当然还得有真实存在的镜像才行),非常方便,不过瓶颈往往在于镜像源。
在上面的脚本中,笔者给出了写文章时还存活的镜像地址。不保证一直可用,失效的时候再去网上找找看,修改配置文件即可。
2.1 安装MySQL
直接run
即可,注意备份实例中的数据和配置,备份数据所在的文件夹需要保证存在
# 建立数据和备份文件所在的目录
sudo mkdir -p /data/mysql/data
sudo mkdir -p /data/mysql/conf
# 指定mysql实例运行的名字,端口号映射等
docker run --name mysql-8.4 \
-e MYSQL_ROOT_PASSWORD=your_own_password \
-v /data/mysql/data:/var/lib/mysql \
-v /data/mysql/conf:/etc/mysql/conf.d \
-d -p 3306:3306 mysql:8.4
# 需要进入
MySQL8.4
似乎不需要写任何配置文件就可正常使用。
2.2 安装Redis
直接run
即可
sudo mkdir -p /data/redis/data
sudo mkdir -p /data/redis/confdocker run --name redis-7.0.15 \
-v /data/redis/data:/data \
-v /data/redis/conf/redis.conf:/usr/local/etc/redis/redis.conf \
-d -p 6379:6379 redis:7.0.15
其中,redis.conf
的内容就是之前在操作系统上直接安装Redis
时的配置文件,去掉注释后如下:
# bind 127.0.0.1 -::1
protected-mode no
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
pidfile /run/redis/redis-server.pid
loglevel notice
logfile /var/log/redis/redis-server.log
databases 16
always-show-logo no
set-proc-title yes
proc-title-template "{title} {listen-addr} {server-mode}"
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
rdb-del-sync-files no
dir /var/lib/redis
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync yes
repl-diskless-sync-delay 5
repl-diskless-sync-max-replicas 0
repl-diskless-load disabled
repl-disable-tcp-nodelay no
replica-priority 100
acllog-max-len 128# 注意这里换成你自己的密码
requirepass {your_own_password}lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
lazyfree-lazy-user-del no
lazyfree-lazy-user-flush no
oom-score-adj no
oom-score-adj-values 0 200 800
disable-thp yes
appendonly no
appendfilename "appendonly.aof"
appenddirname "appendonlydir"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
aof-timestamp-enabled no
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-listpack-entries 512
hash-max-listpack-value 64
list-max-listpack-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-listpack-entries 128
zset-max-listpack-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
jemalloc-bg-thread yes
在运行实例之前需要在/data/redis/conf/
中创建这个文件,并取名为redis.conf
。
3 部署服务
在将你的服务打成jar
包之前,你应该在配置文件中将MySQL
和Redis
的配置项写成环境变量的形式,这既保证信息不泄露,又方便配置管理
# 其它配置省略
spring:datasource:url: jdbc:mysql://${MYSQL_HOST}:3306/cl_db?useSSL=false&serverTimezone=UTCusername: ${MYSQL_USER}password: ${MYSQL_PASSWORD}data:redis:host: ${REDIS_HOST}port: 6379password: ${REDIS_PASSWORD}
使用Docker
的时候,部署服务往往使用Dockerfile
,而Dockerfile
往往是以某个JDK
为基础镜像的,你可以想象成JDK
和服务是绑定在一起的,一个服务对应一个JDK
,下面给出一个Dockerfile
示例:
# 从openjdk17开始构建
FROM openjdk:17
# 镜像信息
# LABEL author="XXX"
# LABEL date=""
# LABEL maintainer=""
# LABEL version="1.0.0-SNAPSHOT"
# LABEL description=""
# 跑一些命令
RUN mkdir -p /app
# 设置环境变量,这里也可以设置服务中具体会使用到的环境变量
ENV PATH="/app:${PATH}"
ENV MYSQL_HOST=mysql-8.4
ENV MYSQL_USER=root
ENV MYSQL_PASSWORD=your_password
ENV REDIS_HOST=redis-7.0.15
ENV REDIS_PASSWORD=your_password
# 指定工作目录
WORKDIR /app
# 将jar包复制进来(<src>相对于Dockerfile所在目录,<dest>相对于工作目录)
COPY your_service.jar .
# 暴露端口,记得加上你的服务的端口
EXPOSE 80 22 443 8080
# 入口
ENTRYPOINT ["java", "-jar", "/app/your_service.jar"]
将jar
包和Dockerfile
放到同一个目录下,然后在这个目录下执行下面的命令将jar
包打成镜像
docker build -t your_images_name:tag .
新建Docker
网络,将服务要使用到的组件连接到这个网络
docker network create your_network
docker network connect your_network mysql-8.4
docker network connect your_network redis-7.0.15
然后将实例运行在这个网络上,这一步可以配置在配置文件中需要用到的环境变量,注意修改成自己的,而且不要忘了端口号,不然外部访问不了。这里指定的环境变量可以将Dockerfile
中的环境变量覆盖掉。
docker run -p 65535:65535 \
--name your_service_name \
--network your_network \
-e MYSQL_HOST=mysql-8.4 \
-e MYSQL_USER=root \
-e MYSQL_PASSWORD=your_password \
-e REDIS_HOST=redis-7.0.15 \
-e REDIS_PASSWORD=your_password \
-d your_images_name:tag
至此,服务部署成功。
4 总结
本文介绍了Docker
的安装以及使用Docker
来部署服务。如果有多个服务,只需要先将jar
包打成镜像,然后根据镜像创建实例即可。不过,美中不足的一点是JDK17
镜像有点大,有471MB
,有兴趣的可以自己尝试优化。(笔者拉了一个78MB
的ubuntu:24.04
的镜像,然后下载jdk17
的安装包传到实例里面安装,最后将这个实例打成镜像,有580MB
。。。)
部署服务到这里就结束了,可以看到比起最初的手动安装,Docker
简化了很多操作,并且使服务和环境方便维护了,当然还有很多很强大的部署和管理服务的工具,比如docker-compose
和k8s
等,但笔者认为作为一个开发者无需去走一遍这些服务部署的实操,只需要在开发中会使用命令即可(当然还得看具体的开发有没有用到这些工具)。
这样,就可以只使用一个虚拟电脑,然后在上面安装Docker
来部署和管理你的服务了。当然关于Docker
还有很多细节这篇文章没有提到,不过部署服务的模式和基本操作差不多就这样。