Docker 是一个快速构建、运行、管理应用的工具,在本文将会学到: 1. 能利用Docker部署常见软件 2. 能利用Docker打包并部署Java应用 3. 理解Docker数据卷的基本作用 4. 能看懂DockerCompose文件
视频链接:https://www.bilibili.com/video/BV1HP4118797/
前置准备
准备Linux环境
安装VMware Workstation
Windows 使用 VMware Workstation 在本地安装一台虚拟机(Windows 10 及以上版本的操作系统需要下载 VMware Workstation 16 Pro 及以上版本)
下载地址: https://www.vmware.com/cn/products/workstation-pro/workstation-pro-evaluation.html
卸载旧版 VMware Workstation(如果有的话): 1. 打开控制面板,卸载 VMware Workstation
- 在原来的安装目录中删除残留的整个 VMware 文件夹
- Win + R
打开面板输入
regedit
调出注册表,打开HKEY_CURRENT_USER
→Software
,找到并删除VMware.Inc
此时可以安装新版 VMware Workstation,如果是卸载重装的情况,可能会存在安装不了虚拟网卡的问题,可以参考该文章:https://www.u72.net/zhishi/show-92927.html
创建虚拟机
- 最大磁盘大小选择 100 GB,并将虚拟磁盘存储为单个文件
- 将内存设置为 8192 MB,CPU核心数设置为 4 核
其他步骤省略...
安装 CentOS 7
- 日期和时间选择亚洲/上海,语言支持选择简体中文
- 系统安装位置选择刚刚添加的磁盘
- 在系统网络和主机名中,打开以太网 ens33,在右下角配置中找到 IPv4 设置,将方法设置为“手动”,在下面添加地址,地址、子网掩码、网关、DNS 服务器可以照抄以太网 ens33的设置,最后修改主机名为 localhost 并应用
- 点击“开始安装”,设置 ROOT 密码
- 可以设置虚拟机快照
安装 SSH 客户端
步骤省略...
安装 Docker
如果系统中存在旧的Docker,则卸载
1
2
3
4
5
6
7
8yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine安装 yum 工具
1
yum install -y yum-utils
配置 Docker 的 yum 源
1
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
安装 Docker
1
yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
启动和校验
1
2
3
4
5
6
7
8
9
10
11
12
13
14# 启动Docker
systemctl start docker
# 停止Docker
systemctl stop docker
# 重启
systemctl restart docker
# 设置开机自启
systemctl enable docker
# 执行docker ps命令,如果不报错,说明安装启动成功
docker ps配置镜像加速 在阿里云中找到“容器镜像服务ACR”,进入控制台
按步骤配置镜像加速器
快速入门
安装 MySQL
过去我们在 Linux 上安装 MySQL 需要搜索并下载 MySQL 安装包、上传至 Linux 环境、编译和配置环境、最后安装
而使用 Docker 安装,仅仅需要一步,在命令行输入以下命令
1
2
3
4
5
6docker run -d \
--name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123 \
mysql
运行效果如图:
MySQL 安装完毕!我们可以使用任何客户端工具,比如 Navicat 连接到 MySQL
当我们利用 Docker 安装应用时,Docker 会自动搜索并下载应用镜像(image)。镜像不仅包含应用本身,还包含应用运行所需要的环境、配置、系统函数库。Docker 会在运行镜像时创建一个独立运行的隔离环境,称为容器(container),因此我们不需要再关注不同的操作系统、不同的运行环境了。
Docker 官方提供了一个专门管理、存储镜像的网站 https://hub.docker.com/ ,并对公众开放了镜像上传和下载的权利。这种提供存储、管理Docker镜像的服务器,被称为DockerRegistry,可以翻译为镜像仓库,像阿里云、华为云等也会提供第三方仓库,我们(或企业)也可以搭建自己私有的镜像仓库。
Docker 服务部署应用时,首先要去搜索并下载应用对应的镜像,然后根据镜像创建并允许容器,应用就部署完成了。用一幅图标识如下:
命令解读
在上面我们执行的命令是什么意思呢? 1
2
3
4
5
6docker run -d \
--name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123 \
mysql
docker run -d
:创建并运行一个容器,-d
则是让容器以后台进程运行,否则 docker 进程将会以“霸屏”的形式打印日志,导致我们无法在该命令行执行其他命令--name mysql
:给容器设置名称为mysql
,任意别名都可以-p 3306:3306
:设置端口映射 我们刚刚知道,容器是个隔离环境,外界是不可访问的。但是可以将宿主机端口映射到容器内的端口,当访问宿主机指定端口时,就是在访问容器内的端口了(比如说 Windows 系统的宿主机无法访问到容器的 3306 号端口,但它可以访问到 CentOS 系统的虚拟机的某某号端口,而虚拟机可以访问到容器的 3306 号端口,因此我们将虚拟机的某某号端口映射到容器的 3306 号端口,那么 Windows 系统的宿主机就可以操控容器了)
容器内端口往往是由容器内的进程决定,例如 MySQL 进程默认端口是 3306,因此容器内端口一定是 3306;而宿主机端口则可以任意指定,一般与容器内保持一致。
格式:-p 宿主机端口:容器内端口
,示例中就是将宿主机(CentOS
虚拟机)的 3306 映射到容器内的 3306 端口
-e TZ=Asia/Shanghai
:配置容器内进程运行时的一些参数(环境变量) 格式:-e KEY=VALUE
,KEY
和VALUE
都由容器内进程决定,需要去镜像仓库参考镜像的使用文档
案例中,TZ=Asia/Shanghai
表示设置时区、MYSQL_ROOT_PASSWORD=123
表示设置默认密码
mysql
:设置镜像名称,Docker 会根据这个名称在镜像仓库中搜索并下载镜像 格式:REPOSITORY:TAG
,表示镜像名称:版本号
,例如mysql:8.0
。TAG
可以省略,默认下载最新版本
- 注意:镜像的名称不是随意的,而是要到 DockerRegistry 中寻找,镜像运行时的配置也不是随意的,要参考镜像的帮助文档,这些在 DockerHub 网站或者软件的官方网站中都能找到。
Docker基础
常见命令
Docker 最常见的命令就是操作镜像和容器的命令,下面这幅图形象地展示了常用命令:
接下来,用一个案例解释上面的常见命令:
在 DockerHub 中搜索 Nginx
1
https://hub.docker.com/_/nginx
拉取 Nginx 镜像
1
docker pull nginx
查看本地镜像列表
1
2
3
4[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 605c77e624dd 23 months ago 141MB
mysql latest 3218b38490ce 24 months ago 516MB打包本地 Nginx 镜像(使用
docker save --help
查看命令详细写法)1
2
3
4
5[root@localhost ~]# docker save -o nginx.tar nginx:latest
[root@localhost ~]# ll
总用量 142492
-rw-------. 1 root root 1324 12月 9 10:51 anaconda-ks.cfg
-rw-------. 1 root root 145905152 12月 11 13:58 nginx.tar删除本地 Nginx 镜像
1
2
3
4
5[root@localhost ~]# docker rmi nginx:latest
...
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql latest 3218b38490ce 24 months ago 516MB解压或加载刚才打包的 Nginx 镜像
1
2
3
4
5
6
7
8
9
10
11
12[root@localhost ~]# docker load -i nginx.tar
2edcec3590a4: Loading layer 83.86MB/83.86MB
e379e8aedd4d: Loading layer 62MB/62MB
b8d6e692a25e: Loading layer 3.072kB/3.072kB
f1db227348d0: Loading layer 4.096kB/4.096kB
32ce5f6a5106: Loading layer 3.584kB/3.584kB
d874fd2bc83b: Loading layer 7.168kB/7.168kB
Loaded image: nginx:latest
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 605c77e624dd 23 months ago 141MB
mysql latest 3218b38490ce 24 months ago 516MB创建并运行 Nginx 容器
1
2[root@localhost ~]# docker run -d --name nginx -p 80:80 nginx
f7a01ce670aaf82481ec6b8d31673b99fa43e6517716887329c19fdb4dbb5917查看当前运行的容器
1
2
3[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f7a01ce670aa nginx "/docker-entrypoint.…" 3 seconds ago Up 3 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp nginx使用格式化的简洁形式查看当前运行的容器
1
2
3[root@localhost ~]# docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"
CONTAINER ID IMAGE PORTS STATUS NAMES
f7a01ce670aa nginx 0.0.0.0:80->80/tcp, :::80->80/tcp Up 5 minutes nginx停止 Nginx 容器
1
2
3
4[root@localhost ~]# docker stop nginx
nginx
[root@localhost ~]# docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"
CONTAINER ID IMAGE PORTS STATUS NAMES查看所有容器(不管是否正在运行)
1
2
3
4[root@localhost ~]# docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}" -a
CONTAINER ID IMAGE PORTS STATUS NAMES
f7a01ce670aa nginx Exited (0) About a minute ago nginx
e60f0af039d7 mysql 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp Exited (255) 35 minutes ago mysql启动 Nginx 容器
1
2
3
4
5[root@localhost ~]# docker start nginx
nginx
[root@localhost ~]# docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"
CONTAINER ID IMAGE PORTS STATUS NAMES
f7a01ce670aa nginx 0.0.0.0:80->80/tcp, :::80->80/tcp Up 5 seconds nginx查看(截至目前的) Nginx 容器的日志
1
2[root@localhost ~]# docker logs nginx
...持续查看 Nginx 容器的日志(使用浏览器访问 Nginx 服务器,使其打印新的日志)
1
2
3
4
5[root@localhost ~]# docker logs -f nginx
...
192.168.50.1 - - [11/Dec/2023:06:31:15 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" "-"
...
^C进入 Nginx 容器内部(
-it
表示添加可输入的命令行终端,bash
表示使用 bash 命令进行交互)进入容器内部后,可以看到我们来到新的终端,输入1
2
3[root@localhost ~]# docker exec -it nginx bash
root@f7a01ce670aa:/# ls
bin boot dev docker-entrypoint.d docker-entrypoint.sh etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr varls
查看当前目录,发现容器相当于模拟了一个新的操作系统环境,我们可以对该系统环境进行修改等操作退出当前 Nginx 容器
1
2
3root@f7a01ce670aa:/# exit
exit
[root@localhost ~]#进入 MySQL 容器,并使用 MySQL 命令操作数据库
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[root@localhost ~]# docker start mysql
mysql
[root@localhost ~]# docker exec -it mysql bash
root@e60f0af039d7:/# ls
bin boot dev docker-entrypoint-initdb.d entrypoint.sh etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@e60f0af039d7:/# mysql -uroot -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 8.0.27 MySQL Community Server - GPL
Copyright (c) 2000, 2021, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.02 sec)
mysql> exit
Bye
root@e60f0af039d7:/# exit
exit
[root@localhost ~]#删除容器(运行中的容器无法直接删除,可以使用
-f
强制删除)1
2
3
4
5
6
7
8
9
10
11
12[root@localhost ~]# docker rm nginx
Error response from daemon: You cannot remove a running container f7a01ce670aaf8248
1ec6b8d31673b99fa43e6517716887329c19fdb4dbb5917. Stop the container before attempti
ng removal or force remove
[root@localhost ~]# docker stop nginx
nginx
[root@localhost ~]# docker rm nginx
nginx
[root@localhost ~]# docker rm mysql -f
mysql
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
小技巧:为命令赋别名
格式化查看本地镜像的命令太长了,我们可以为该命令赋别名,以后每次都不需要输入过长的命令了(这是 Linux 的使用技巧,而非 Docker)
打开并编辑 bashrc 文件
1
[root@localhost ~]# vi ~/.bashrc
添加一条别名配置
1
alias dps='docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"'
使 bashrc 文件生效
1
[root@localhost ~]# source ~/.bashrc
使用我们自定义的别名
1
2[root@localhost ~]# dps
CONTAINER ID IMAGE PORTS STATUS NAMES
数据卷挂载
如果我们想要在 Nginx
容器保存静态资源的目录中进行操作,比如vi index.html
,我们发现甚至都没有vi
命令,因为
Docker 仅准备了运行环境的依赖
因此我们需要依靠数据卷(volume),使其作为容器内目录和宿主机目录互相映射的桥梁,其工作原理如下图所示:
所以,当我们修改了宿主机目录中的文件,其所对应的容器目录也会随之修改
数据卷常用命令
命令 | 说明 |
---|---|
docker volume create |
创建数据卷 |
docker volume ls |
查看所有数据卷 |
docker volume rm |
删除指定数据卷 |
docker volume inspect |
查看某个数据卷的详情 |
docker volume prune |
清除数据卷 |
案例:修改Nginx容器的静态资源文件
创建 Nginx 容器并挂载数据卷
注意:已经创建好的容器无法再挂载数据卷了;当创建容器时,如果挂载了数据卷且数据卷不存在,会自动创建数据卷,不需要再手动1
2[root@localhost ~]# docker run -d --name nginx -p 80:80 -v html:/usr/share/nginx/html nginx
74932b6a045639d39fb280b8a64af0465027de7951ef703b5d793920367fa4c0docker volume create
查看所有数据卷
1
2
3
4[root@localhost ~]# docker volume ls
DRIVER VOLUME NAME
local 984db09343b3b4dacf5df0b7c3990ba58317ef83b57da1de3ad1d852ab97e19d
local html查看数据卷详情
1
2
3
4
5
6
7
8
9
10
11
12[root@localhost ~]# docker volume inspect html
[
{
"CreatedAt": "2023-12-11T15:26:47+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/html/_data",
"Name": "html",
"Options": null,
"Scope": "local"
}
]进入数据卷文件夹,修改
index.html
,并添加一张图片1
[root@localhost ~]# cd /var/lib/docker/volumes/html/_data
在浏览器中访问 Nginx 服务器,可以看到修改后的
index.html
和添加的图片进入 Nginx 容器保存静态资源的目录,可以看到添加的图片
1
2
3
4[root@localhost _data]# docker exec -it nginx bash
root@74932b6a0456:/# cd /usr/share/nginx/html
root@74932b6a0456:/usr/share/nginx/html# ls
1.jpg 50x.html index.html
挂载本地目录或文件
可以发现,数据卷的目录结构较深,如果我们去操作数据卷目录会不太方便。而且比如 MySQL 容器在创建时其实会自动生成匿名的数据卷,如果当我们需要创建新的 MySQL容器时,前一个自动生成的匿名卷不会给新的容器使用,所以在数据迁移上并不方便。在很多情况下,我们会直接将容器目录与宿主机指定目录挂载
挂载语法与数据卷类似: 1
2
3
4
5# 挂载本地目录
-v 本地目录:容器内目录
# 挂载本地文件
-v 本地文件:容器内文件
注意:本地目录或文件必须以/
或./
开头,否则会视为数据卷名称,Docker
会在它的数据卷目录下创建该数据卷
案例:创建 MySQL 容器并挂载本地目录
删除并重新创建 MySQL 容器,并完成本地目录挂载
- 挂载
/root/mysql/data
到容器内的/var/lib/mysql
目录 - 挂载
/root/mysql/init
到容器内的/docker-entrypoint-initdb.d
目录(初始化的SQL脚本目录) - 挂载
/root/mysql/conf
到容器内的/etc/mysql/conf.d
目录(MySQL 配置文件目录)
- 准备初始化 SQL 脚本和 MySQL 配置文件,将两个文件分别置于 init
文件夹和 conf 文件夹
1
2
3
4
5
6
7
8[client]
default_character_set=utf8mb4
[mysql]
default_character_set=utf8mb4
[mysqld]
character_set_server=utf8mb4
collation_server=utf8mb4_unicode_ci
init_connect='SET NAMES utf8mb4'
1 | -- 导出 hmall 的数据库结构 |
删除原来的 MySQL 容器
1
2[root@localhost ~]# docker rm mysql
mysql进入 root 目录,并创建本地目录,将另两个文件夹也上传至 mysql 目录内
1
2
3
4[root@localhost ~]# cd ~
[root@localhost ~]# mkdir mysql
[root@localhost ~]# cd mysql
[root@localhost mysql]# mkdir data创建并运行新的 MySQL 容器,挂载本地目录
1
2
3
4
5
6
7
8
9[root@localhost mysql]# docker run -d \
--name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123 \
-v /root/mysql/data:/var/lib/mysql \
-v /root/mysql/conf:/etc/mysql/conf.d \
-v /root/mysql/init:/docker-entrypoint-initdb.d \
mysql观察本地
/mysql/data
目录,出现了初始化脚本生成的数据库,在 Navicat 中也可以连接并访问数据库;使用show variables like "%char%";
查看编码表,发现编码是 utf8mb4 没有问题
镜像
镜像结构
如果我们从零部署一个 Java 应用,大致流程如下: 1. 准备 Linux 服务器 2. 安装并配置 JDK 3. 上传 Jar 包 4. 运行 Jar 包
因此,我们打包镜像也是分为如下几部: 1. 准备 Linux 运行环境(只需要基础运行环境,而不需要完整的操作系统) 2. 安装并配置 JDK 3. 拷贝 Jar 包 4. 配置启动脚本
上述步骤中的每一次操作其实都是在生产一些文件(系统运行环境、函数库、配置最终都是磁盘文件),所以镜像就是一堆文件的集合
但需要注意的是,镜像文件不是随意堆放的,而是按照操作的步骤分层叠加而成,每一层形成的文件都会单独打包并标记一个唯一id,称为Layer(层)。这样,如果我们构建时用到的某些层其他人已经制作过,就可以直接拷贝使用这些层,而不用重复制作。
例如,第一步中需要的 Linux 运行环境,通用性就很强,所以 Docker 官方就制作了这样的只包含 Linux 运行环境的镜像。我们在制作java镜像时,就无需重复制作,直接使用 Docker 官方提供的 CentOS 或 Ubuntu 镜像作为基础镜像。然后再搭建其它层即可,这样逐层搭建,最终整个 Java 项目的镜像结构如图所示:
Dockerfile
由于制作镜像的过程中,需要逐层处理和打包,比较复杂,所以 Docker 就提供了自动打包镜像的功能。我们只需要将打包的过程,每一层要做的事情用固定的语法写下来,交给 Docker 去执行即可
而这种记录镜像结构的文件就称为 Dockerfile,其对应的语法可以参考官方文档: https://docs.docker.com/engine/reference/builder/
其中语法比较多,但比较常用的有:
指令 | 说明 | 示例 |
---|---|---|
FROM |
指定基础镜像 | FROM centos:6 |
ENV |
设置环境变量,可在后面指令使用 | ENV key value |
COPY |
拷贝本地文件到镜像的指定目录 | COPY ./xx.jar /tmp/app.jar |
RUN |
执行 Linux 的 Shell 指令,一般是安装过程的命令 | RUN yum install gcc |
EXPOSE |
指定容器运行时监听的端口,是给镜像使用者看的 | EXPOSE 8080 |
ENTRYPOINT |
镜像中应用的启动命令,容器运行时调用 | ENTRYPOINT java -jar xx.jar |
例如,要基于 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 环境,我们在此基础上制作 Java
镜像,就可以省去 JDK 和 Linux 的配置了: 1
2
3
4
5
6
7
8
9# 基础镜像
FROM openjdk:11.0-jre-buster
# 设定时区
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 文件写好以后,就可以利用命令来构建镜像了
准备一个 demo 项目及对应的 Dockerfile 文件
1
2
3
4
5
6
7
8
9# 基础镜像
FROM openjdk:11.0-jre-buster
# 设定时区
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"]将 docker-demo.jar 包以及 Dockerfile 文件上传至
/root/demo
目录我们还需要加载 JDK 运行环境的基础镜像
1
2
3
4
5
6
7
8[root@localhost ~]# docker load -i jdk.tar
2c7e7ab2260a: Loading layer 119.3MB/119.3MB
9ad2165feb02: Loading layer 17.18MB/17.18MB
92903c3857f8: Loading layer 17.87MB/17.87MB
1736ab871b32: Loading layer 12.18MB/12.18MB
6f8e4cb95a88: Loading layer 3.584kB/3.584kB
41080a0c646f: Loading layer 141.8MB/141.8MB
Loaded image: openjdk:11.0-jre-buster最终构建镜像
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23[root@localhost ~]# cd demo
[root@localhost demo]# docker build -t docker-demo .
[+] Building 0.5s (8/8) FINISHED docker:default
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 359B 0.0s
=> [internal] load metadata for docker.io/library/openjdk:11.0-jre-buster 0.0s
=> [1/3] FROM docker.io/library/openjdk:11.0-jre-buster 0.0s
=> [internal] load build context 0.1s
=> => transferring context: 17.70MB 0.1s
=> [2/3] RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && ec 0.3s
=> [3/3] COPY docker-demo.jar /app.jar 0.1s
=> exporting to image 0.1s
=> => exporting layers 0.1s
=> => writing image sha256:ff64b16c8d9e97cb388f50870f7777dbd41d4b65ec359abc 0.0s
=> => naming to docker.io/library/docker-demo 0.0s
[root@localhost demo]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker-demo latest ff64b16c8d9e 29 seconds ago 319MB
nginx latest 605c77e624dd 23 months ago 141MB
mysql latest 3218b38490ce 24 months ago 516MB
openjdk 11.0-jre-buster 57925f2e4cff 2 years ago 301MB
命令说明: - docker build
:表示构建一个 Docker 镜像 -
-t docker-demo:1.0
:-t
参数用于指定镜像的名称和版本号(repository
和tag
)
- .
:最后的点表示构建 Dockerfile
所在的路径,由于我们当前进入了
demo
目录,所以.
表示当前目录,也可以直接指定
Dockerfile 目录
1 | # 直接指定Dockerfile目录 |
使用该镜像创建并运行容器
1
2
3
4
5[root@localhost demo]# docker run -d --name dd -p 8080:8080 docker-demo
0472d44ee1db631fbbf6d7682033a61a25faea4bbe482dc189a7235120162b64
[root@localhost demo]# dps
CONTAINER ID IMAGE PORTS STATUS NAMES
0472d44ee1db docker-demo 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp Up 6 seconds dd查看容器的运行日志,在浏览器中可以访问到该容器
1
[root@localhost demo]# docker logs -f dd
网络
在 Java 项目中往往需要访问其他中间件,例如 MySQL、Redis
等。现在,我们的容器之间能否互相访问呢?我们测试一下: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# 1.用基本命令查看MySQL容器,寻找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
但是,容器的网络IP其实是一个虚拟的IP,其值并不固定与某一个容器绑定,如果我们在开发时写死某个IP,而在部署时很可能 MySQL 容器的IP会发生变化,连接会失败。
所以,我们必须借助于 Docker 的网络功能来解决这个问题,官方文档: https://docs.docker.com/engine/reference/commandline/network/
常见命令有:
命令 | 说明 |
---|---|
docker network create |
创建一个网络 |
docker network ls |
查看所有网络 |
docker network rm |
删除指定网络 |
docker network prune |
清除未使用的网络 |
docker network connect |
使指定容器连接加入某网络 |
docker network disconnect |
使指定容器连接离开某网络 |
docker network inspect |
查看网络详细信息 |
默认情况下,所有容器都是以网桥 bridge 方式连接到 Docker 的一个虚拟网桥上:
我们也可以使用自定义网络,这样做的好处是可以直接使用容器名进行网络连接,而不是
IP 地址 1. 创建自定义网络 1
2[root@localhost ~]# docker network create myNet
934a2dc1fab3c142b5f9b2c0fc91fa2610c2f5c18effe2b6268c7e1848b93327
查看当前 Docker 网络
1
2
3
4
5
6[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
f9d9be2137db bridge bridge local
e499842177d3 host host local
934a2dc1fab3 myNet bridge local
8b5b67afe065 none null local在宿主机上也可以看到创建出来的虚拟网卡
1
2
3
4
5
6
7
8
9[root@localhost ~]# ip addr
...
4: br-934a2dc1fab3: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue stat
e DOWN group default
link/ether 02:42:38:34:a3:5f brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-934a2dc1fab3
valid_lft forever preferred_lft forever查看 MySQL 当前的网络 IP 属性
1
2
3
4
5# 用基本命令,寻找Networks.bridge.IPAddress属性
[root@localhost ~]# docker inspect mysql
# 也可以使用format过滤结果
[root@localhost ~]# docker inspect --format='{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' mysql
172.17.0.2使 MySQL 容器加入自定义网络,同时我们可以
--alias
为容器赋别名1
[root@localhost ~]# docker network connect myNet mysql
再次查看 MySQL 当前的网络IP属性
1
2
3[root@localhost ~]# docker inspect --format='{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' mysql
172.17.0.2
172.18.0.2删除现有的 dd 容器,我们采用另一种方式使 dd 容器加入自定义网络
查看 dd 容器的网络IP属性,我们发现 dd 容器是直接加入了自定义网络,而没有加入默认网络。因此,当我们创建并运行容器时,如果没有指定网络,那么也会加入默认网络1
2
3
4
5
6[root@localhost ~]# docker rm -f dd
dd
[root@localhost ~]# docker run -d --name dd -p 8080:8080 --network myNet docker-demo
5a637b87f878f364023d91358e2601722031a2dc22178533d87eb1814ac6414d
[root@localhost ~]# docker inspect --format='{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' dd
172.18.0.3进入 dd 容器,访问 MySQL 容器
1
2
3
4
5
6
7
8
9
10
11[root@localhost ~]# docker exec -it dd bash
root@5a637b87f878:/# ping mysql
PING mysql (172.18.0.2) 56(84) bytes of data.
64 bytes from mysql.myNet (172.18.0.2): icmp_seq=1 ttl=64 time=0.064 ms
64 bytes from mysql.myNet (172.18.0.2): icmp_seq=2 ttl=64 time=0.055 ms
64 bytes from mysql.myNet (172.18.0.2): icmp_seq=3 ttl=64 time=0.172 ms
64 bytes from mysql.myNet (172.18.0.2): icmp_seq=4 ttl=64 time=0.158 ms
^C
--- mysql ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 4ms
rtt min/avg/max/mdev = 0.055/0.112/0.172/0.053 ms
总结: - 在自定义网络中,可以给容器起多个别名,默认的别名是容器名本身 - 在同一个自定义网络中的容器,可以通过别名互相访问
项目部署
OK~ 我们已经熟悉了 Docker 的基本用法,接下来可以尝试部署一个完整项目了
这个项目中包括: - 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
部署 Java 项目
查看 hmall
项目的 application.yaml
配置文件,我们发现其中的 JDBC 地址并未写死,而是读取配置文件中的变量
1
2
3
4
5
6
7
8
9
10spring:
application:
name: hm-service
profiles:
active: dev # 选择dev开发环境
datasource:
url: jdbc:mysql://${hm.db.host}:3306/hmall?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: ${hm.db.pw}
这两个变量在 application-dev.yaml 和 application-local.yaml
中并不相同 1
2
3
4hm:
db:
host: mysql
pw: 1231
2
3
4hm:
db:
host: localhost
pw: 123 # 本地MySQL密码
使用 Maven 打包项目,然后将打包好的 Jar 包和 Dockerfile 文件一并上传至服务器
1
2
3
4
5
6
7
8
9# 基础镜像
FROM openjdk:11.0-jre-buster
# 设定时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 拷贝jar包
COPY hm-service.jar /app.jar
# 入口
ENTRYPOINT ["java", "-jar", "/app.jar"]构建项目镜像,如果没有指定
tag
,则默认为latest
1
2
3[root@localhost ~]# mkdir myJavaProjects
[root@localhost ~]# cd myJavaProjects/
[root@localhost myJavaProjects]# docker build -t hmall .创建并运行容器,注意通过
--network
将其加入自定义网络myNet
,这样才能通过容器名访问 mysql 容器1
2
3
4
5
6[root@localhost myJavaProjects]# docker run -d --name hm -p 8080:8080 --network myNet hmall
09ec826ef84f5764df82cf43c85c244283d67a9adc9c4d04f37c932e9726e1f5
[root@localhost myJavaProjects]# dps
CONTAINER ID IMAGE PORTS STATUS NAMES
09ec826ef84f hmall 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp Up 9 seconds hm
f517c745019d mysql 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp Up 24 minutes mysql使用
docker logs -f hm
命令持续查看 hm 容器的日志,然后通过浏览器访问http://虚拟机IP:8080/search/list
进行测试
部署前端项目
hmall-portal
和hmall-admin
是前端代码,需要基于
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
47worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/json;
sendfile on;
keepalive_timeout 65;
server {
listen 18080;
# 指定前端项目所在的位置
location / {
root /usr/share/nginx/html/hmall-portal;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
location /api {
rewrite /api/(.*) /$1 break;
proxy_pass http://hm:8080;
}
}
server {
listen 18081;
# 指定前端项目所在的位置
location / {
root /usr/share/nginx/html/hmall-admin;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
location /api {
rewrite /api/(.*) /$1 break;
proxy_pass http://hm:8080;
}
}
}/
路径跳转至服务器中的静态资源目录,对/api
路径反向代理至后端服务器
后端项目已经部署完毕,所以现在要将静态文件和 Nginx 配置文件上传至服务器中,这是 nginx 文件夹结构:nginx.conf 是 Nginx 配置文件,html 文件夹是前端项目
将整个 nginx 文件夹上传至服务器的
root
目录在 Nginx 官方镜像的使用文档中可以看到配置文件需要挂载到
/etc/nginx/nginx.conf
,前端项目需要挂载到/usr/share/nginx/html
。而且由于需要让 Nginx 同时代理hmall-portal
和hmall-admin
两套前端资源,因此我们需要暴露两个端口:
- 18080:对应
hmall-portal
- 18081:对应
hmall-admin
1 | docker run -d \ |
- 最后通过浏览器访问
http://虚拟机IP:18080
或18081
进行测试
DockerCompose
从上面可以看出,如果我们想部署一个简单的 Java 项目,其中包括三个容器:MySQL、Nginx、Java 项目。而稍微复杂的项目,其中还会有其他各种各样的中间件,如果还像之前手动地逐一部署,就太麻烦了。
而 Docker Compose 可以实现多个相互关联的 Docker 容器的快速部署。它允许用户通过一个 docker-compose.yml 模板文件来定义一组相关联的应用容器
基础语法
使用文档:https://docs.docker.com/compose/compose-file/compose-file-v3/
docker-compose
文件中可以定义多个相互关联的应用容器,每一个应用容器被称为一个服务(service)。由于
service
就是在定义某个应用的运行时参数,因此与docker run
参数非常相似。
举例来说,使用docker run
命令部署MySQL的命令如下:
1
2
3
4
5
6
7
8
9
10docker run -d \
--name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123 \
-v ./mysql/data:/var/lib/mysql \
-v ./mysql/conf:/etc/mysql/conf.d \
-v ./mysql/init:/docker-entrypoint-initdb.d \
--network hmall
mysql
如果使用docker-compose.yml
文件来定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19version: "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 |
网络 |
下面是部署上文的 Java 项目的docker-compose
文件
1 | version: "3.8" |
基础命令
官方文档:https://docs.docker.com/compose/reference/
基础语法如下: 1
docker compose [OPTIONS] [COMMAND]
其中,OPTIONS和COMMAND都是可选参数,比较常见的有:
|类型|参数或指令|说明| |:--:|:--:|:--:|
|OPTIONS
|-f
|指定 compose 文件的路径和名称|
|OPTIONS
|-p
|指定 project 名称。project
就是当前 compose 文件中设置的多个 service 的集合,是逻辑概念|
|COMMAND
|up
|创建并启动所有service容器|
|COMMAND
|down
|停止并移除所有容器、网络|
|COMMAND
|ps
|列出所有启动的容器|
|COMMAND
|logs
|查看指定容器的日志|
|COMMAND
|stop
|停止容器|
|COMMAND
|start
|启动容器|
|COMMAND
|restart
|重启容器|
|COMMAND
|top
|查看运行的进程|
|COMMAND
|exec
|在指定的运行中容器中执行命令|
把 docker-compose 文件上传至 root 目录
进入 root 目录
1
cd /root
删除旧容器
1
docker rm -f $(docker ps -qa)
删除 hmall 镜像
1
docker rmi hmall
清空 MySQL 数据
1
rm -rf mysql/data
运行 docker-compose 文件,
-d
表示后台启动所有容器1
docker compose up -d
查看镜像
1
docker compose images
查看容器
1
docker compose ps
打开浏览器访问
http://服务器IP:8080
进行测试