Docker

Docker安装

卸载

如果之前有安装过旧版本的Docker,则通过命令卸载旧版本的Docker

1
2
3
4
5
6
7
8
9
10
11
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine \
docker-ce

没有任何匹配,说明没装过docker

安装

首先需要虚拟机联网,安装yum工具

1
2
3
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2 --skip-broken

运行耐心等待一下,安装完成(最后显示 完毕!)

然后更新本地镜像源:配置阿里云的仓库

1
2
3
4
5
6
7
8
# 设置docker镜像源
yum-config-manager \
--add-repo \
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo

yum makecache fast

安装docker

1
yum install -y docker-ce

docker-ce: 社区版

启动Docker

防火墙设置

1
2
3
4
5
6
# 关闭
systemctl stop firewalld
# 禁止开机启动防火墙
systemctl disable firewalld
# 可以查看防火墙状态
systemctl status firewalld

启动

1
2
3
4
5
systemctl start docker  # 启动docker服务

systemctl stop docker # 停止docker服务

systemctl restart docker # 重启docker服务

查看docker状态

1
systemctl status docker

查看docker版本

1
docker -v

配置镜像加速

docker官方镜像仓库网速较差,我们需要设置国内镜像服务:
参考阿里云的镜像加速文档:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors

1
2
3
4
5
6
7
8
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://gtxivg4h.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

部署MySQL

先停掉虚拟机中的MySQL,确保虚拟机已经安装Docker,且网络开通的情况下,执行下面命令即可安装MySQL

1
2
3
4
5
6
docker run -d \
--name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123456 \
mysql

解读:

docker run -d :创建并运行一个容器,**-d**则是让容器以后台进程运行

–name mysql : 给容器起个名字叫mysql,必须唯一

-p 3306:3306 : 设置端口映射。(后面这个是容器内MySQL默认端口,前面是映射出去的端口(宿主机),比如3307)

容器是隔离环境,外界不可访问。但是可以将宿主机端口映射容器内到端口,当访问宿主机指定端口时,就是在访问容器内的端口了。

容器内端口往往是由容器内的进程决定,例如MySQL进程默认端口是3306,因此容器内端口一定是3306;而宿主机端口则可以任意指定,一般与容器内保持一致。

格式: -p 宿主机端口:容器内端口,示例中就是将宿主机的3306映射到容器内的3306端口

-e TZ=Asia/Shanghai : 配置容器内进程运行时的一些参数(e–>environment)

格式:**-e KEY=VALUE**,KEY和VALUE都由容器内进程决定

案例中,TZ=Asia/Shanghai是设置时区;MYSQL_ROOT_PASSWORD=123456是设置MySQL默认密码

mysql : 设置镜像名称,Docker会根据这个名字搜索并下载镜像

格式:REPOSITORY:TAG,例如mysql:8.0,其中REPOSITORY可以理解为镜像名,TAG是版本号

在未指定TAG的情况下,默认是最新版本,也就是mysql:latest

连接远程数据库

打开数据库连接工具

如果出现没有权限,则在Docker容器中,输入指令,进入容器,查看容器内目录

1
docker exec -it container_name /bin/bash

登录MySQL

1
mysql -u root -p

修改权限

1
2
3
4
5
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
# 更新user为root,host为% 的密码为123456

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';
# 更新user为root,host为localhost 的密码为123456

刷新权限

1
FLUSH PRIVILEGES; #刷新权限

最后查询用户信息 (查看root用户)

1
select host,user,plugin,authentication_string from mysql.user;

连接成功则可以像在Windows操作本地数据库一样的了

Docker基础

常见命令

命令 说明
docker pull 拉取镜像
docker push 推送镜像到DockerRegistry
docker images 查看本地镜像
docker rmi 删除本地镜像
docker run 创建并运行容器(不能重复创建)
docker stop 停止指定容器
docker start 启动指定容器
docker restart 重新启动容器
docker rm 删除指定容器
docker ps 查看容器
docker logs 查看容器运行日志
docker exec 进入容器
docker save 保存镜像到本地压缩文件
docker load 加载本地压缩文件到镜像
docker inspect 查看容器详细信息
docker stats 查看容器资源使用情况(通过容器id)

Docker文档Docker Docs

案例

以拉取Nginx为例:

需求:

  • 在DockerHub中搜索Nginx镜像,查看镜像的名称
  • 拉取Nginx镜像
  • 查看本地镜像列表
  • 创建并运行Nginx容器
  • 查看容器
  • 停止容器
  • 再次启动容器
  • 进入Nginx容器
  • 删除容器
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
# 1.访问Docker Hub搜索镜像

# 2.拉取Nginx镜像,不指定版本即默认为latest最新
docker pull nginx

# 3.查看本地镜像列表
docker images
# 结果如下
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 605c77e624dd 22 months ago 141MB
mysql latest 3218b38490ce 22 months ago 516MB

# 4.创建并运行Nginx容器
docker run -d --name nginx -p 80:80 nginx

# 5.查看容器
docker ps
# 5.1查看容器格式化(格式会更加清爽)
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"
# 结果
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b83c1bfa9956 nginx "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp nginx

[root@localhost ~]# docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"
CONTAINER ID IMAGE PORTS STATUS NAMES
b83c1bfa9956 nginx 0.0.0.0:80->80/tcp, :::80->80/tcp Up 2 minutes nginx

# 6.停止容器
docker stop nginx

# 6.1查看所有容器 docker ps -a
docker ps -a --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"
# 结果:(刚刚停止的Nginx的状态为Exited)
[root@localhost ~]# docker ps -a --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"
CONTAINER ID IMAGE PORTS STATUS NAMES
b83c1bfa9956 nginx Exited (0) 3 minutes ago nginx
107a163cba71 mysql 3306/tcp, 33060/tcp Created xenodochial_allen
b32527ef7193 mysql Exited (0) 25 hours ago mysql

# 7.启动容器
docker start nginx
# 8.进入容器,查看容器内目录
docker exec -it nginx bash # ecec:执行 -it:模拟可输入终端 bash:命令行交互

# 9.查看日志
docker logs nginx # 查看nginx日志
docker logs -f nginx # 一直查看Nginx日志

# 10.退出容器
exit

# 11.删除容器
docker rm nginx # 在运行的容器删除会提示
# 无法删除,则需要先停止(stop在rm),或者
docker rm -f nginx

命令别名

比如在前面的案例中,我们需要对docker ps显示的内容进行格式化(format),指令过长,每一次都要输入这么多, 非常麻烦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 修改/root/.bashrc文件
vi /root/.bashrc
内容如下:
# .bashrc

# User specific aliases and functions

alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
alias dps='docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"'
alias dis='docker images'

# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi

然后,执行命令使别名生效:

1
source /root/.bashrc

所以当我们设置了这个, 如果需要查看镜像的情况, 则可以使用dps格式化别名指令

数据卷

容器是隔离环境,容器内程序的文件、配置、运行时产生的容器都在容器内部,我们要读写容器内的文件非常不方便 (比如我们进入到容器中使用vi指令修改index.html会提示没有指令).

数据卷(volume)是一个虚拟目录,是容器内目录宿主机目录之间映射的桥梁

以Nginx为例,众所周知

  • html: 放静态资源
  • conf: 放配置文件

如果我们要让Nginx代理我们的静态资源,最好是放到html目录;如果我们要修改Nginx的配置,最好是找到conf下的nginx.conf文件

但是非常遗憾的一点是, 不能直接去修改它(原容器), 容器运行的Nginx所有的文件都在容器内部。所以我们必须利用数据卷将两个目录与宿主机目录关联,方便我们操作。如图:

上图解答:

我们创建了两个数据卷html和conf

其中html对应nginx容器中的/html, 通过数据卷指向了宿主机中的文件/html, _data是用来存放数据自动创建的, 这样一来容器和宿主机就被关联起来了了, 称之为挂载

这样我们就可以在宿主机中的 /var/lib/docker/volumes/html/_data 存放资源, 那么容器中的数据也会被容器相对应的目录所映射

当前操作为 双向绑定, 宿主机自动映射到容器

/var/lib/docker/volumes目录为默认存放所有容器数据卷的目录, 在下根据数据卷的名称创建新目录, 例如: /数据卷名称/_data

数据卷命令

命令 说明
docker volume create 创建数据卷
docker volume ls 查看所有数据卷
docker volume rm 删除指定数据卷
docker volume inspect 查看某个数据卷的详情
docker volume prune 清除数据卷

Docker文档Docker Docs

注意:容器与数据卷的挂载要在创建容器时配置,对于创建好的容器,是不能设置数据卷的。而且创建容器的过程中,数据卷会自动创建

案例

需求;

  • 创建Nginx容器, 修改nginx容器内的html目录下的index.html文件内容
  • 将静态资源部署到nginx的html目录

提示: 在执行docker run 命令时, 使用 -v 数据卷:容器内目录 可以完成数据卷挂载

当创建容器时, 如果挂载了数据卷且数据卷不存在, 则会自动创建数据卷

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
# 1.首先创建容器并指定数据卷,注意通过 -v 参数来指定数据卷
docker run -d --name nginx -p 80:80 -v html:/usr/share/nginx/html nginx

# 2.然后查看数据卷
docker volume ls
# 结果
[root@localhost ~]# docker volume ls
DRIVER VOLUME NAME
local a8bcfc131404273c3d5a5a450fbcc270dbf414d1367648190e864eebfc7802ad
local html # 刚刚创建的卷在这

# 3.查看数据卷详情
docker volume inspect html
# 结果
[root@localhost ~]# docker volume inspect html
[
{
"CreatedAt": "2023-11-05T21:18:52+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/html/_data", # 宿主机位置(映射)
"Name": "html",
"Options": null,
"Scope": "local"
}
]

# 4.查看/var/lib/docker/volumes/html/_data目录
[root@localhost ~]# ll /var/lib/docker/volumes/html/_data
总用量 8
-rw-r--r--. 1 root root 497 12月 28 2021 50x.html
-rw-r--r--. 1 root root 615 12月 28 2021 index.html

# 5.进入该目录,并随意修改index.html内容
[root@localhost ~]# cd /var/lib/docker/volumes/html/_data
#修改
[root@localhost _data]# vi index.html

# 6.进入容器内部,查看/usr/share/nginx/html目录内的文件是否变化
docker exec -it nginx bash

cd /usr/share/nginx/html

如果不指定挂载目录,自动会为你挂载到/usr/lib下

挂载本地目录或文件

数据卷的目录结构较深,如果我们去操作数据卷目录会不太方便。在很多情况下,我们会直接将容器目录与宿主机指定目录挂载。挂载语法与数据卷类似:

1
2
3
4
# 挂载本地目录
-v 本地目录:容器内目录
# 挂载本地文件
-v 本地文件:容器内文件
注意: 本地目录或文件必须以 `/` 或 `./`开头,如果直接以名字开头,会被识别为数据卷名而非本地目录名。
1
2
-v mysql:/var/lib/mysql # 会被识别为一个数据卷叫mysql,运行时会自动创建这个数据卷
-v ./mysql:/var/lib/mysql # 会被识别为当前目录下的mysql目录,运行时如果不存在会创建目录

案例需求:

  • 挂载/root/mysql/data到容器内的/var/lib/mysql目录
  • 挂载/root/mysql/init到容器内的/docker-entrypoint-initdb.d目录(初始化的SQL脚本目录 —> 只有第一次才生效)
  • 挂载/root/mysql/conf到容器内的/etc/mysql/conf.d目录(这个是MySQL配置文件目录)

解释: init为SQL脚本初始化, conf为配置文件, 其中配置文件主要是配置一些MySQL默认编码, utf8mb4

我们在虚拟机创建对应的目录,data, init, conf

本地目录挂载:(关于MySQL的挂载详情直达MySQL Image | Docker Hub)

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
# 1.删除原来的MySQL容器
docker rm -f mysql

# 2.进入root目录(:需要设置挂载的目录,提前创建好挂载目录)
cd ~

# 3.创建并运行新的MySQL容器,并且挂载(-v)到本地目录
# 当前使用的路径为全路径 /root/mysql, 也可以./root/mysql
docker run -d \
--name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123456 \
-v /root/mysql/data:/var/lib/mysql \
-v /root/mysql/conf:/etc/mysql/conf.d \
-v /root/mysql/init:/docker-entrypoint-initdb.d \
mysql

# 4.查看MySQL容器内数据
# 查看data目录,会发现里面有大量数据库数据,说明数据库完成了初始化
ls -l data
# 4.1 进入MySQL
docker exec -it mysql bash
# 4.2 登录mysql
# 4.3 查看编码表
show variables like "%char%"
# 结果,发现编码是预设的utf8mb4没有问题
+--------------------------+--------------------------------+
| Variable_name | Value |
+--------------------------+--------------------------------+
| character_set_client | utf8mb4 |
| character_set_connection | utf8mb4 |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | utf8mb4 |
| character_set_server | utf8mb4 |
| character_set_system | utf8mb3 |
| character_sets_dir | /usr/share/mysql-8.0/charsets/ |
+--------------------------+--------------------------------+

# 5.查看数据(其实就是SQL语言了)

镜像

在这之前, 前面都是pull别人的镜像, 如果我也需要部署一个自己的镜像, 那么该怎么做呢?

镜像结构

在构建镜像之前, 我们需要先了解镜像的结果.

镜像之所以可以让我们快速跨操作系统部署应用而忽略其运行环境、配置,就是因为镜像中包含了程序运行需要的系统函数库、环境、配置、依赖。

所以不容易看出, 自定义镜像的本质就是依次准备好程序运行的基础环境、依赖、应用本身、运行配置等文件,并且打包而成

镜像是由层级结构组成: (在我们pull镜像的时候就不难看出)

Dockerfile

Dockerfile就是一个文本文件, 其中包含了一个个的指令(Instruction), 用指令来说明要执行什么操作来构建镜像。将来Docker可以根据Dockerfile来帮我们构建镜像。常见指令如下:

指令 说明 示例
FROM 指定基础镜像 from centos:6
ENV 设置环境变量,可在后面指令使用 enu alue
COPY 拷贝本地文件到镜像的指定目录 copy ./xx.jar /tmp/app.jar
RUN 执行Linux的shell命令,一般是安装过程的命令 run yum install gcc
EXPOSE 指定容器运行时监听的端口,是给镜像使用者看的 expose 8080
ENTRYPOINT 镜像中应用的启动命令,容器运行时调用 entrypoint java -jar xx.jar

更多详细语法, 请参考官方文档 Dockerfile reference | Docker Docs

例如需要基于Ubuntu构建一个Java应用, 其中Dockerfile内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 指定基础镜像
FROM ubuntu:16.04
# 配置环境变量,JDK的安装目录、容器内时区
ENV JAVA_DIR=/usr/local
ENV TZ=Asia/Shanghai
# 拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar
# 设定时区
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 安装JDK
RUN cd $JAVA_DIR \
&& tar -xf ./jdk8.tar.gz \
&& mv ./jdk1.8.0_144 ./java8
# 配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin
# 指定项目监听的端口
EXPOSE 8080
# 入口,java项目的启动命令
ENTRYPOINT ["java", "-jar", "/app.jar"]

对于JDK镜像配置, 我们可以使用别人提供的JDK基础镜像, 简化Dockerfile

1
2
3
4
5
6
7
8
9
# 基础镜像
FROM openjdk:11.0-jre-buster # 省去JDk配置
# 设定时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 拷贝jar包
COPY docker-demo.jar /app.jar
# 入口
ENTRYPOINT ["java", "-jar", "/app.jar"]

最后相对于上面那个, 下面这个简洁了很多了

构建镜像

当Dockerfile编写好之后, 就可以使用命令来构建镜像了, 例如在当前目录下准备好了一个Dockerfile文件和ClickCount.jar项目jar包

1
2
3
4
# 进入存放文件的目录
cd /root/demo
# 开始构建
docker build -t click:1.0 .

命令说明:

  • docker build: 构建一个docker镜像
  • -t:是给镜像起名, 格式一人撒repository:tag的格式, 如果不指定tag,则默认为latest
  • .:最后的点代表当前目录(Dockerfile所在目录), 也可以指定其他目录的Dockerfile
1
2
# 例如指定/root/dockerfile_demo目录
docker build -t click:1.0 /root/dockerfile_dem

查看镜像列表

1
2
3
4
5
6
7
# 查看镜像列表:
docker images
# 结果
REPOSITORY TAG IMAGE ID CREATED SIZE
click 1.0 d6ab0b9e64b9 10 minutes ago 89MB
nginx latest 605c77e624dd 16 months ago 141MB
mysql latest 3218b38490ce 17 months ago 517MB

网络

在上面的案例, 我们已经创建了Java项目的容器, 其中往往Java项目需要访问其他容器中间件(MySQL,Redis等), 他们需要访问则需要通过网络

默认情况下, 所有容器都是以bridge(网桥)方式连接到Docker的一个虚拟网桥上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1.用基本命令,寻找Networks.bridge.IPAddress属性
docker inspect mysql
# 也可以使用format过滤结果
docker inspect --format='{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' mysql
# 得到IP地址如下:
172.17.0.2

# 2.然后通过命令进入dd容器
docker exec -it dd bash

# 3.在容器内,通过ping命令测试网络
ping 172.17.0.2
# 结果
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.053 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.059 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.058 ms

发现Ping通, 并没有什么问题

但是,容器的网络IP其实是一个虚拟的IP,其值并不固定与某一个容器绑定,如果我们在开发时写死某个IP,而在部署时很可能MySQL容器的IP会发生变化,连接会失败

官方文档docker network | Docker Docs

常见网络命令有:

命令 说明 文档地址
docker network create 创建一个网络 docker network create
docker network ls 查看所有网络 docs.docker.com
docker network rm 删除指定网络 docs.docker.com
docker network prune 清除未使用的网络 docs.docker.com
docker network connect 使指定容器连接加入某网络 docs.docker.com
docker network disconnect 使指定容器连接离开某网络 docker network disconnect
docker network inspect 查看网络详细信息 docker network inspect

自定义网络

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
# 1.首先通过命令创建一个网络
docker network create qlh

# 2.然后查看网络
docker network ls
# 结果:
NETWORK ID NAME DRIVER SCOPE
e070f0159014 bridge bridge local
cfb0c477c4dc host host local
9393a7da1c50 none null local
f5084a25485a qlh bridge local
# 其中,除了qlh以外,其它都是默认的网络

# 3.让dd和mysql都加入该网络,注意,在加入网络时可以通过--alias给容器起别名
# 这样该网络内的其它容器可以用别名互相访问!
# 3.1.mysql容器,指定别名为db,另外每一个容器都有一个别名是容器名
docker network connect qlh mysql --alias db
# 3.2.db容器,也就是我们的java项目
docker network connect qlh dd

# 4.进入dd容器,尝试利用别名访问db
# 4.1.进入容器
docker exec -it dd bash
# 4.2.用db别名访问
ping db
# 结果
PING db (172.18.0.2) 56(84) bytes of data.
64 bytes from mysql.qlh (172.18.0.2): icmp_seq=1 ttl=64 time=0.070 ms
64 bytes from mysql.qlh (172.18.0.2): icmp_seq=2 ttl=64 time=0.056 ms
# 4.3.用容器名访问
ping mysql
# 结果:
PING mysql (172.18.0.2) 56(84) bytes of data.
64 bytes from mysql.qlh (172.18.0.2): icmp_seq=1 ttl=64 time=0.044 ms
64 bytes from mysql.qlh (172.18.0.2): icmp_seq=2 ttl=64 time=0.054 ms

现在可以通过别名(例如: db)访问

总结:

  • 在自定义网络中,可以给容器起多个别名,默认的别名是容器名本身
  • 在同一个自定义网络中的容器,可以通过别名互相访问

(旧)项目部署示例

尝试部署黑马商城

项目结构:

  • hmall: 商城的后端代码
  • hmall-portal:商城用户端的前端代码
  • hmall-admin:商城管理端的前端代码

部署的容器及端口说明:

项目 容器名 端口 备注
hmall hmall 8080 黑马商城后端API入口
hmall-portal nginx 18080 黑马商城用户端入口
hmall-admin nginx 18081 黑马商城管理端入口
mysql mysql 3306 数据库

在正式部署前,我们先删除之前的nginx、dd两个容器:

1
docker rm -f nginx dd

mysql容器中已经准备好了商城的数据,所以就不再删除了。

部署SpringBoot项目

hamll项目是一个maven聚合项目, 打开项目结构如下


hamll

—–>hm-common 通用工具模块

—–>hm-service 业务模块


其中我们需要部署的是hm-service模块, 其中配置文件采用了多环境的方式

  • application.yml
  • application-dev.yml
  • application-local.yml

其中的application-dev.yaml是部署到开发环境的配置,application-local.yaml是本地运行时的配置

查看application.yaml,会发现其中的JDBC地址并未写死,而是读取变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 启动端口
server:
port: 9000
spring:
# 环境修改
profile:
active: dev
# 数据库相关 1. db.host 2. db.pw
datasource:
url: jdbc:mysql://${db.host}:3306/hmall?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=true
username: root
password: ${db.pw}
driver-class-name: com.mysql.cj.jdbc.Driver

这两个变量在application-dev.ymlapplication-local.yml中并不相同:

1
2
3
4
5
6
7
8
9
# application-dev.yml
db:
host: mysql
pw: 123

# application-local.yml
db:
host: localhost
pw: 123456

在dev开发环境(也就是Docker部署时)采用了mysql作为地址,刚好是我们的mysql容器名,只要两者在一个网络,就一定能互相访问

hm-service目录下的Dockerfilehm-service/target目录下的hm-service.jar一起上传到虚拟机的root目录

部署项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1.构建项目镜像,不指定tag,则默认为latest
docker build -t hmall .

# 2.查看镜像
docker images
# 结果
REPOSITORY TAG IMAGE ID CREATED SIZE
hmall latest 0bb07b2c34b9 43 seconds ago 362MB
docker-demo 1.0 49743484da68 24 hours ago 327MB
nginx latest 605c77e624dd 16 months ago 141MB
mysql latest 3218b38490ce 17 months ago 516MB

# 3.创建并运行容器,并通过--network将其加入hmall网络,这样才能通过容器名访问mysql
docker run -d --name hmall --network hmall -p 8080:8080 hmall

测试,通过浏览器访问:http://你的虚拟机地址:8080/search/list。

部署前端

hmall-portalhmall-admin是前端代码,需要基于nginx部署。在黑马资料中已经提供了nginx的部署目录

其中:

  • html是静态资源目录,我们需要把hmall-portal以及hmall-admin都复制进去
  • nginx.conf是nginx的配置文件,主要是完成对html下的两个静态资源目录做代理

我们现在要做的就是把整个nginx目录上传到虚拟机的/root目录下:

然后创建nginx容器并完成两个挂载:

  • /root/nginx/nginx.conf挂载到/etc/nginx/nginx.conf
  • /root/nginx/html挂载到/usr/share/nginx/html

由于需要让nginx同时代理hmall-portal和hmall-admin两套前端资源,因此我们需要暴露两个端口:

  • 18080:对应hmall-portal
  • 18081:对应hmall-admin

命令如下:

1
2
3
4
5
6
7
8
docker run -d \
--name nginx \
-p 18080:18080 \
-p 18081:18081 \
-v /root/nginx/html:/etc/nginx/html \
-v /root/nginx/nginx.conf:/etc/nginx/nginx.conf \
--network hmall \
nginx

测试,通过浏览器访问:http://你的虚拟机ip:18080

(新)项目部署

在Windows和Linux下部署其实是一样的,首先我们需要将Dockerfileproject.jar放在一个目录下面

例如Dockerfile示例如下

1
2
3
4
5
6
7
8
9
10
11
12
# 添加 Java 8 镜像来源
FROM registry.cn-beijing.aliyuncs.com/hub-mirrors/openjdk:8-jdk-alpine
#WORKDIR指令用于指定容器的一个目录, 容器启动时执行的命令会在该目录下执行。
WORKDIR /opt/docker/images/calyeedatabases/

# 添加 Spring Boot 包
ADD chat-server-1.0-SNAPSHOT.jar /chat-server-1.0-SNAPSHOT.jar

EXPOSE 8080
EXPOSE 8090
# 执行启动命令 /chat-server-1.0-SNAPSHOT.jar需要和上面的一样
ENTRYPOINT ["java","-jar","/chat-server-1.0-SNAPSHOT.jar"]

然后jar包也在当前目录,例如 chat-server-1.0-SNAPSHOT.jar

然后我们需要输入指令

1
docker build -f Dockerfile  -t  calyee/calyeechat:1.0  .

然后就可以看到有镜像(calyee/calyeechat:1.0)了

我们需要将镜像跑起来

1
docker run -d -p 8080:8080 -p 8090:8090 --name calyeechat calyee/calyeechat:1.0

在当前项目我们开了两个端口 所以需要映射两个端口出去(当前我们没有用到网桥,如需使用网桥可以参考整合下面小结#FastDFS)

然后 docker start calyeechat即可,如果我们没有运行成功,需要查看报错信息,可以使用docker logs 容器id.

DockerCompose

在当前的项目部署示例中, 我们部署了 1)MySQL容器 2)Nginx容器 3) Java项目, 但是在以后复杂的项目中, 当然还要部署其他的中间件, 从而远远不止类似于当前的三个容器, 如果还像之前那样部署, 非常麻烦

而Docker Compose就可以帮助我们实现多个相互关联的Docker容器的快速部署。它允许用户通过一个单独的 docker-compose.yml 模板文件(YAML 格式)来定义一组相关联的应用容器

基本语法

Docker Compose的YML语法详情见Compose file version 3 reference | Docker Docs

在docker-compose文件中可以定义多个相互关联的应用容器,每一个应用容器被称为一个服务

在之前的MySQL部署时, 我们使用的命令示例是这样的

1
2
3
4
5
6
7
8
9
10
docker run -d \
--name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123456 \
-v ./mysql/data:/var/lib/mysql \
-v ./mysql/conf:/etc/mysql/conf.d \
-v ./mysql/init:/docker-entrypoint-initdb.d \
--network hmall
mysql

而使用Compose, 是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
version: "3.8"

services:
mysql:
image: mysql
container_name: mysql
ports:
- "3306:3306"
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: 123
volumes:
- "./mysql/conf:/etc/mysql/conf.d"
- "./mysql/data:/var/lib/mysql"
networks:
- new
networks:
new:
name: hmall

对比如下:

docker run 参数 docker compose 指令 说明
–name container_name 容器名称
-p ports 端口映射
-e environment 环境变量
-v volumes 数据卷配置
–network networks 网络

那么对于刚刚项目部署的样例则可以修改为

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
version: "3.8"

services:
mysql:
image: mysql
container_name: mysql
ports:
- "3306:3306"
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: 123456
volumes:
- "./mysql/conf:/etc/mysql/conf.d"
- "./mysql/data:/var/lib/mysql"
- "./mysql/init:/docker-entrypoint-initdb.d"
networks:
- hm-net
hmall:
build:
context: .
dockerfile: Dockerfile
container_name: hmall
ports:
- "8080:8080"
networks:
- hm-net
depends_on:
- mysql
nginx:
image: nginx
container_name: nginx
ports:
- "18080:18080"
- "18081:18081"
volumes:
- "./nginx/nginx.conf:/etc/nginx/nginx.conf"
- "./nginx/html:/usr/share/nginx/html"
depends_on:
- hmall
networks:
- hm-net
networks:
hm-net:
name: hmall

基本命令

编写好了Yaml文件, 就可以部署项目了

对于一些常见的命令 Overview of docker compose CLI | Docker Docs

语法如下

1
docker compose [OPTIONS] [COMMAND]

其中,OPTIONS和COMMAND都是可选参数,比较常见的有:

类型

参数或指令

说明

Options

-f

指定compose文件的路径和名称

-p

指定project名称。project就是当前compose文件中设置的多个service的集合,是逻辑概念

Commands

up

创建并启动所有service容器

down

停止并移除所有容器、网络

ps

列出所有启动的容器

logs

查看指定容器的日志

stop

停止容器

start

启动容器

restart

重启容器

top

查看运行的进程

exec

在指定的运行中容器中执行命令

样例: Docker整合FastDFS

镜像

1
2
3
4
# 1. 查找FastDFS
docker search fastdfs
# 2. 拉取镜像
docker pull delron/fastdfs

tracker安装

1
docker run -d --name tracker --network=yournetwork -v E:\dockercontainers\fastdfs\tracker:/var/fdfs delron/fastdfs tracker
  • E:\dockercontainers\fastdfs\tracker为挂载到本地的路径
  • --network=yournetwork加入的网桥

storage安装

1
docker run -d -p 8888:8888 --name storage --network=yournetwork  -e TRACKER_SERVER=172.18.0.2:22122  -v E:\dockercontainers\fastdfs\storage:/var/fdfs -e GROUP_NAME=group1 delron/fastdfs storage
  • TRACKER_SERVER为上面的tracker连接到的网桥的地址
  • GROUP_NAME分组名

测试: 在storage容器中测试使用命令上传图片

1
2
3
4
5
6
7
8
9
10
# 进入容器内bash指令模式
docker exec -it storage bash
# 进入/var/fdfs目录
cd /var/fdfs
# 加载位于E:\dockercontainers\fastdfs\storage下的文件 3.png
# E:\dockercontainers\fastdfs\storage为映射地址
# 加载完成会返回一串字符路径 group1/M00/00/00/rBIAA2VLaFSAXi66AABGbuqCV2g017.jpg
/usr/bin/fdfs_upload_file /etc/fdfs/client.conf 3.png
# 最后通过映射端口访问
http://localhost:8888/group1/M00/00/00/rBIAA2VLaFSAXi66AABGbuqCV2g017.jpg

应用: SpringBoot整合FastDFS

导入第三方依赖

1
2
3
4
5
6
<!--fastDFS-->
<dependency>
<groupId>com.luhuiguo</groupId>
<artifactId>fastdfs-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>

配置SpringBoot Yaml

1
2
3
4
5
fdfs:
# docker网桥分配的IP地址 tracker的地址
tracker-list: 172.18.0.4:22122
so-timeout: 6000 # 设置读取时间
connect-timeout: 1000 # 设置连接时间

编写一个简单的UploadService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 注入Fast客户端
@Autowired
private FastFileStorageClient fastFileStorageClient;

public String uploadFile(MultipartFile multipartFile) {
StorePath storePath = null;
try {
storePath = fastFileStorageClient.uploadFile("",
multipartFile.getInputStream()
, multipartFile.getSize()
, "jpg");
log.info("Group:{},Path:{},FullPath:{}",
storePath.getGroup(),
storePath.getPath(),
storePath.getFullPath());
} catch (IOException e) {
log.error("uploadFile发生了异常,{}", e.getMessage());
return null;
}
return storePath.getFullPath();
}

该starter源码参考: [luhuiguo](luhuiguo/fastdfs-spring-boot-starter: Spring Boot FastDFS Starter (github.com)), 关于此依赖版本, 参考maven仓库, 搜索fastdfs (当前github中的readme文件介绍指定的是0.1.0)

应用通信

环境: SpringBoot项目,Docker容器有: Redis, MySQL, FastDFS

当前项目的拓扑图示例如下:

  • 对于Windows访问容器: Windows访问暴露的端口, 例如访问docker容器中的nginx暴露的8888端口, 从而访问FastDFS存储的照片

  • 对于Docker容器内部通信: 容器之间可以通过网桥通信, 例如我的Application应用(SpringBoot), 需要访问MySQL和Redis等容器, 则可以访问网桥分配的ip地址(如图), 实现通信。查看如何查询网桥情况: docker network inspect your_bridge

关于线上环境: 在Docker内部则是通过网桥访问容器原生端口(例如MySQL:3306), SpringBoot的配置文件配置样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fdfs:
# docker网桥分配的IP地址 tracker的地址
tracker-list: 172.18.0.4:22122
so-timeout: 6000 # 设置读取时间
connect-timeout: 1000 # 设置连接时间
server:
port: 9999 # 服务端口
spring:
datasource:
# docker网桥分配的IP地址
url: jdbc:mysql://172.18.0.2:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
#Redis相关配置
redis:
host: 172.18.0.5 # docker网桥分配的IP地址
port: 6379
#password:123456
database: 0 #0是0号数据库,redis默认开启的16个数据库
timeout: 5000 #超时时间

例如Redis在网桥上的IP为172.18.0.5, 则需要在配置文件中配置网桥地址。 (结构图参考该小结首照片)