Docker入门学习
Docker学习笔记
1.快速入门
准备:linux服务器or虚拟机,OS--Centos7。
安装Docker - 飞书云文档 (feishu.cn) 或者
Linux安装Docker完整教程-腾讯云开发者社区-腾讯云 (tencent.com)
有时候,Docker 官方仓库可能暂时不可用,可以使用镜像源来代替。例如:
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
1.1部署MySQL
使用docker安装MySQL:
docker run -d \
--name mysql01 \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123456 \
mysql:8.0.20
- 镜像和容器
当我们利用Docker安装应用时,Docker会自动搜索并下载应用镜像(image)。镜像不仅包含应用本身,还包含应用运行所需要的环境、配置、系统函数库。Docker会在运行镜像时创建一个隔离环境,称为容器(container)。
镜像仓库:存储和管理镜像的平台,Docker官方维护了一个公共仓库:hub.docker.com
1.2命令解读
docker run -d \
--name mysql01 \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123456 \
mysql:8.0.20
命令 | 描述 |
---|---|
docker run | 创建一个新的容器并运行 |
-d | 让容器后台运行 |
\ | shell命令换行 |
--name [容器名称] | 设置启动容器的名称,必须唯一 |
-p [宿主机端口]:[容器端口] | 设置端口映射,将容器的端口映射到主机的端口 |
-e KEY=VALUE | environment,设置环境变量 |
-e MYSQL_ROOT_PASSWORD=[密码] 设置root用户的密码 |
|
-e TZ=Asia/Shanghai 设置时区 |
|
[repository]:[tag] | 指定运行的镜像的名字,repository就是镜像名,tag是镜像版本,如mysql:8.0.20 。不指定tag则默认为latest,最新版本的镜像 |
2.Docker基础
2.1常见命令
Docker最常见的命令就是操作镜像、容器的命令,详见官方文档:Docker Docs
例子:查看dockerHub,拉取nginx镜像,创建并运行Nginx容器
- 在DockerHub中搜索Nginx镜像,查看镜像的名称
- 拉取Nginx镜像
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
a2abf6c4d29d: Pull complete
a9edb18cadd1: Pull complete
589b7251471a: Pull complete
186b1aaa4aa6: Pull complete
b4df32aa5a72: Pull complete
a0bcbecc962e: Pull complete
Digest: sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
- 查看本地镜像列表
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest d2c94e258dcb 8 months ago 13.3kB
nginx latest 605c77e624dd 2 years ago 141MB
mysql 8.0.20 be0dbf01a0f3 3 years ago 541MB
如果要保存下载的镜像,可以使用docker save
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker save --help
Usage: docker save [OPTIONS] IMAGE [IMAGE...]
Save one or more images to a tar archive (streamed to STDOUT by default)
Aliases:
docker image save, docker save
Options:
-o, --output string Write to a file, instead of STDOUT
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker save -o nginx.tar nginx:latest
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# ll
总用量 142488
-rw------- 1 root root 145902080 1月 20 21:30 nginx.tar
如果docker中没有镜像或者删除了,可以使用docker load 重新加载镜像
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# 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
- 创建并运行Nginx容器
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker run -d --name nginx01 -p 80:80 nginx
bb29e54fbd8362a84a8eb7a1ab594310b9f3d518d5e9d841977fec512739a45c
- 查看容器
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bb29e54fbd83 nginx "/docker-entrypoint.…" 50 seconds ago Up 49 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp nginx01
da18e9eda14c mysql:8.0.20 "docker-entrypoint.s…" About an hour ago Up About an hour 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp mysql01
docker ps 命令是查看运行中的容器,如果要查看所有容器,需要添加参数 docker ps -a
- 停止容器
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker stop nginx01
nginx01
- 再次启动容器
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker start nginx01
nginx01
持续查看容器日志:docker logs -f [容器名称]
- 进入Nginx容器
docker exec -it [容器名称] [命令]
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker exec -it nginx01 bash
root@bb29e54fbd83:/#
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker exec --help
Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
Execute a command in a running container
Aliases:
docker container exec, docker exec
Options:
-d, --detach Detached mode: run command in the background
--detach-keys string Override the key sequence for detaching a container
-e, --env list Set environment variables
--env-file list Read in a file of environment variables
-i, --interactive Keep STDIN open even if not attached
--privileged Give extended privileges to the command
-t, --tty Allocate a pseudo-TTY
-u, --user string Username or UID (format: "<name|uid>[:<group|gid>]")
-w, --workdir string Working directory inside the container
退出用exit
- 删除容器
删除容器前需要停止容器,否则报错
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker rm nginx01
Error response from daemon: cannot remove container "/nginx01": container is running: stop the container before removing or force remove
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker stop nginx01
nginx01
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker rm nginx01
nginx01
- 命令别名
如果觉得docker的命令太长,可以到 ~/.bashrc 文件中添加命令的别名,简化命令
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# vim ~/.bashrc
保存文件后,使用 source ~/.bashrc 使其生效
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# source ~/.bashrc
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# dis
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest d2c94e258dcb 8 months ago 13.3kB
nginx latest 605c77e624dd 2 years ago 141MB
mysql 8.0.20 be0dbf01a0f3 3 years ago 541MB
2.2数据卷
容器是隔离环境,容器内程序的文件、配置、运行时产生的容器都在容器内部,要读写容器内的文件非常不方便。
- 如果要升级MySQL版本,需要销毁旧容器,那么数据岂不是跟着被销毁了?
- MySQL、Nginx容器运行后,如果我要修改其中的某些配置该怎么办?
- 想要让Nginx代理我的静态资源怎么办?
因此,容器提供程序的运行环境,但是程序运行产生的数据、程序运行依赖的配置都应该与容器解耦。
2.2.1什么是数据卷?
数据卷(volume)是一个虚拟目录,是容器内目录与宿主机目录之间映射的桥梁。方便我们操作容器内文件,或者方便迁移容器产生的数据。
比如一个nginx容器中,有两个文件 /etc/nginx/conf
和 usr/share/nginx/html
,但在容器内修改非常麻烦。有了数据卷,我们就可以将数据卷和容器内的目录进行挂载。
步骤如下:
-
首先使用docker命令创建两个数据卷 html 和 conf 。
docker 会自动帮我们在宿主机文件系统中创建两个真实的目录
/var/lib/docker/volumes/html/_data
和/var/lib/docker/volumes/conf/_data
。数据卷html会映射到真实目录下的.../html/_data
,conf 则映射到真实目录下的.../conf/_data
。每一个数据卷都和宿主机上的一个目录进行一一对应。 -
创建数据卷后,让容器目录跟数据卷做挂载。即将容器的目录和数据卷进行关联,这样一来容器的conf目录指向了conf数据卷,conf 数据卷又指向了宿主文件系统的 .../conf/_data 目录,容器的目录就间接地和宿主机的目录产生了关联。一旦关联之后,docker就会自动进行容器内目录和宿主机目录之间的双向绑定和双向映射。
需要注意,数据卷的挂载只能在创建容器的时候进行。如果容器已经创建了是没有办法再去做挂载的。
-
如何挂载数据卷?
-
在创建容器时,利用
-v 数据卷名:容器内目录
完成挂载 -
容器创建时,如果挂载的数据卷不存在,docker将会自动创建
-
-
数据卷的常见命令
命令 释义 docker volume create 创建数据卷 docker volume ls 查看数据卷 docker volume rm 删除指定数据卷 docker volume inspect 查看数据卷详情 docker volume prune 删除所有未使用的数据卷
案例1-利用Nginx容器部署静态资源
需求:
- 创建Nginx容器,修改nginx容器内的html目录下的index.html文件,查看变化
- 将静态资源部署到nginx的html目录
[root@iZ7xvgwzig5m0o0rnf19bkZ _data]# docker run -d --name nginx02 -p 80:80 -v html:/usr/share/nginx/html nginx #创建容器nginx02,并挂载
5d4b9dcde08b22f40769f6b1cb8e1133ec6a982f695952a168b6cd6d47d311fe
[root@iZ7xvgwzig5m0o0rnf19bkZ _data]# docker ps #查看运行的容器
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5d4b9dcde08b nginx "/docker-entrypoint.…" 4 seconds ago Up 4 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp nginx02
da18e9eda14c mysql:8.0.20 "docker-entrypoint.s…" 2 days ago Up 8 hours 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp mysql01
[root@iZ7xvgwzig5m0o0rnf19bkZ _data]# docker volume ls #查看数据卷
DRIVER VOLUME NAME
local c2699d6d6375a6100df53cb01bc3c8db94316d05c120cf7f93dba0dc334b8f17
local html
[root@iZ7xvgwzig5m0o0rnf19bkZ _data]# docker volume inspect html #查看数据卷详情
[
{
"CreatedAt": "2024-01-22T22:12:39+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/html/_data",
"Name": "html",
"Options": null,
"Scope": "local"
}
]
[root@iZ7xvgwzig5m0o0rnf19bkZ _data]# cd /var/lib/docker/volumes/html/_data/
[root@iZ7xvgwzig5m0o0rnf19bkZ _data]# ls #容器目录的文件映射到此目录下
50x.html index.html
在映射的宿主文件系统目录中修改,可以改变容器目录的文件。同样的修改容器文件,也可以影响宿主目录文件。实现了宿主机目录与容器内目录的双向绑定。
- 匿名数据卷
使用命令 docker inspect [容器名称]
查看MySQL容器详细信息
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker inspect mysql01
[
{
...
"Mounts": [
{
"Type": "volume",
"Name": "c2699d6d6375a6100df53cb01bc3c8db94316d05c120cf7f93dba0dc334b8f17",
"Source": "/var/lib/docker/volumes/c2699d6d6375a6100df53cb01bc3c8db94316d05c120cf7f93dba0dc334b8f17/_data",
"Destination": "/var/lib/mysql",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
...
"Config": {
...
"Volumes": {
"/var/lib/mysql": {}
},
...
}
}
}
]
Volumes 部分:可以发现这个容器声明了一个本地目录,需要挂载数据卷,但是数据卷未定义。这就是匿名卷
Mounts 部分:可以发现其中有几个关键属性
- Name:数据卷名称。由于定义的容器未设置数据卷名,这里的就是匿名卷自动生成的名字,一串hash值
- Source:宿主机目录
- Destination:容器内的目录
上述配置将容器内的 /var/lib/mysql
目录,与数据卷 c2699d6d6375a6100df53cb01bc3c8db94316d05c120cf7f93dba0dc334b8f17
挂载。于是在宿主机中就有了 /var/lib/docker/volumes/c2699d6d6375a6100df53cb01bc3c8db94316d05c120cf7f93dba0dc334b8f17/_data
这个目录,即匿名数据卷对应的目录,其使用方式与普通数据卷没有区别。
每一个不同的镜像,将来创建容器后内部有哪些目录可以挂载,可以参考DockerHub对应的页面
2.2.2挂载本地目录或文件(推荐)
可以发现数据卷的目录结构比较深,如果直接去操作不太方便,在很多情况下我们会直接将容器目录和宿主机指定的目录挂载。
挂载语法和数据卷类似:在执行 docker run
命令时,使用 -v [本地目录]:[容器内目录]
可以完成本地目录挂载
#挂载本地目录
-v [本地目录]:[容器内目录]
#挂载本地文件
-v [本地文件]:[容器内文件]
注意:本地目录或文件必须以 /
或 ./
开头,如果直接以名字开头会被识别为数据卷名而非本地目录名。
-v mysql:/var/lib/mysql # 会被识别为一个数据卷叫mysql,运行时会自动创建这个数据卷
-v ./mysql:/var/lib/mysql # 会被识别为当前目录下的mysql目录,运行时如果不存在会创建目录
案例2-mysql容器的数据挂载
需求:
- 查看mysql容器,判断是否有数据卷挂载
- 基于宿主机目录实现MySQL数据目录、配置文件、初始化脚本的挂载(查阅官方镜像文档 https://hub.docker.com/)
查阅官方镜像文档知道mysql容器的文件位置分别为:配置文件目录:/etc/mysql/conf.d
,初始化脚本目录:/docker-entrypoint-initdb.d
,数据目录:/var/lib/mysql
。
挂载 /root/mysql/data
到容器内的 /var/lib/mysql
目录
挂载 /root/mysql/init
到容器内的 /docker-entrypoint-initdb.d
目录
挂载 /root/mysql/conf
到容器内的 /etc/mysql/conf.d
目录
#首先创建 init,data,conf目录,然后将准备好的conf,init文件放到对应目录中
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# tree /root
/root
└── mysql
├── conf
│ └── hm.cnf
├── data
└── init
└── hmall.sql
4 directories, 2 files
#然后创建容器,挂载到本地目录
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker run -d \
> --name mysql01 \
> -p 3306:3306 \
> -e TZ=Asia/Shanghai \
> -e MYSQL_ROOT_PASSWORD=123456 \
> -v /root/mysql/data:/var/lib/mysql \
> -v /root/mysql/init:/docker-entrypoint-initdb.d \
> -v /root/mysql/conf:/etc/mysql/conf.d \
> mysql:8.0.20
4f764809252683e4116fa35a24133039b8d82b39e975edf228adc5c0e681954c
#查看 /root/mysql/下的目录,可以看到很多文件,说明挂载成功了
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# tree /root/mysql
本地宿主机目录挂载到容器中,可以方便自定义目录,并且当容器删除后,数据仍可以持久化保存。
比如这里如果将mysql容器删除,只要再次运行创建mysql容器时将之前的本地目录挂载,之前的所有数据依然可以恢复。
2.3自定义镜像
2.3.1镜像的结构
镜像之所以能让我们快速跨操作系统部署应用而忽略其运行环境、配置,就是因为镜像中包含了程序运行需要的系统函数库、环境、配置、依赖。
因此,自定义镜像本质就是依次准备好程序运行的基础环境、依赖、应用本身、运行配置等文件,并且打包而成。
如上,构建一个Java镜像的步骤和部署一个Java应用的步骤类似。而构建镜像的每一步其实都是在生产一些文件(系统运行环境、函数库、配置这些最终都是磁盘文件),所以镜像就是一堆文件的集合。
但是镜像文件不是随意堆放的,而是按照操作的步骤分层叠加而成,每一层形成的文件都会单独打包并标记一个唯一id,称为Layer(层)。照此理解,如果构建镜像时需要用到的其他层别人已经制作过,就可以直接拷贝使用这些层,而不用重复制作。
比如第一步中需要用到的Linux运行环境,因为通用性很强,所以docker官方已经制作了这样的只包含Linux运行环境的镜像。在制作Java镜像时,就无需重复制作,直接使用Docker官方提供的Centos或Ubuntu镜像作为基础镜像(BaseImage)。然后再搭建其他层即可,这样逐层搭建,最终的整个Java项目的镜像如下所示:
2.3.2Dockerfile
Dockerfile就是一个文本文件,其中包含一个个的指令(Instruction),用指令来说明要执行什么操作来构建镜像。将来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 |
2.3.3构建镜像
我们可以基于Ubuntu基础镜像,利用dockerfile描述镜像结构,也可以直接基于JDK为基础镜像,省略前面的步骤:
写好Dockerfile后可以使用以下命令来构建镜像:
docker build -t [镜像名] [Dockerfile所在路径]
- 例子
dockerfile:
# 基础镜像
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和要运行的jar包放到指定目录,我这里是 /root/demo下
[root@iZ7xvgwzig5m0o0rnf19bkZ demo]# ls /root/demo
docker-demo.jar Dockerfile
第一步:下载jar包,然后制作成基础镜像
[root@iZ7xvgwzig5m0o0rnf19bkZ demo]# 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
[root@iZ7xvgwzig5m0o0rnf19bkZ demo]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
openjdk 11.0-jre-buster 57925f2e4cff 2 years ago 301MB
第二步:构建镜像
[root@iZ7xvgwzig5m0o0rnf19bkZ demo]# docker build -t mydockerimage .
[+] Building 1.6s (8/8) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 299B 0.0s
=> [internal] load metadata for docker.io/library/openjdk:11.0-jre-buster 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/3] FROM docker.io/library/openjdk:11.0-jre-buster 0.3s
=> [internal] load build context 0.3s
=> => transferring context: 17.70MB 0.3s
=> [2/3] RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo Asia/Shanghai 0.8s
=> [3/3] COPY docker-demo.jar /app.jar 0.2s
=> exporting to image 0.1s
=> => exporting layers 0.1s
=> => writing image sha256:1b3d36d47dc40e5b7b5e6685d4797b86867ee8cb7876e84a4cf697428123a8e4 0.0s
=> => naming to docker.io/library/mydockerimage 0.0s
这样自定义镜像就成功了。
#我们查看docker镜像仓库,自定义构建的镜像已经存在了
[root@iZ7xvgwzig5m0o0rnf19bkZ demo]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mydockerimage latest 1b3d36d47dc4 About a minute ago 319MB
openjdk 11.0-jre-buster 57925f2e4cff 2 years ago 301MB
#创建运行我们自定义的容器
[root@iZ7xvgwzig5m0o0rnf19bkZ demo]# docker run -d --name test01 -p 8080:8080 mydockerimage
8093e17b7659a8d9a6338d193dbce8ed3e52bad24bd0dc17454e914a9fad2a85
[root@iZ7xvgwzig5m0o0rnf19bkZ demo]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8093e17b7659 mydockerimage "java -jar /app.jar" 9 seconds ago Up 8 seconds 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp test01
也可以使用 docker logs -f [容器名]
查看日志。
2.4网络
默认情况下,所有容器都是以bridge方式连接到docker的一个虚拟网桥上:
但容器的网络IP其实是一个虚拟的IP,其值不与某一个固定的的容器绑定,这意味这如果我们重启了某个容器,docker给这个容器分配的IP就有可能变化,对于我们在开发中写死的IP来说,就意味着容器重启后可能会连接不上。
因此,我们必须借助docker的网络功能来解决这个问题:
加入自定义网络的容器才可以通过容器名相互访问,不需要对方的IP地址,从而解决了容器IP变化带来的问题。
Docker的网络操作命令如下:
命令 | 说明 | 文档地址 |
---|---|---|
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 |
- 例子
#创建网络
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker network create mynet
bb39d29512dcf79b8da15314ccfc9d80a72646b09a0b191baa19f8e7fe0e0e88
#查看所有网络
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
46fcecfd0e3b bridge bridge local
9c77bd6498c1 host host local
bb39d29512dc mynet bridge local
e6f0fc103f47 none null local
#将mysql01容器连接加入到mynet网络中
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker network connect mynet mysql01
#或者指定别名为db,默认每一个容器都有一个别名,即它本身的容器名
#docker network connect hmall mysql --alias db
使用 docker inspect mysql01
可以看到该容器已经加入了新的网络中:
- 也可以在容器创建的时候就可以创建一个网络,并且让容器加入该网络:
docker run -d --name [容器名] -p [主机端口]:[容器端口] --network [网络名] [镜像名]
如:
docker run -d --name test01 -p 8080:8080 --network mynet01 mydockerimage
使用 docker inspect test01
可以发现该容器只有指定的网桥:
进入test01容器内部,使用ping [容器名]
的方式,可以直接连接同一指定网络下的mysql01容器:
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker exec -it test01 bash
root@a7c3a0c6041c:/# ping mysql01
PING mysql01 (172.18.0.2) 56(84) bytes of data.
64 bytes from mysql01.mynet (172.18.0.2): icmp_seq=1 ttl=64 time=0.054 ms
64 bytes from mysql01.mynet (172.18.0.2): icmp_seq=2 ttl=64 time=0.060 ms
64 bytes from mysql01.mynet (172.18.0.2): icmp_seq=3 ttl=64 time=0.072 ms
^C
--- mysql01 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2ms
rtt min/avg/max/mdev = 0.054/0.062/0.072/0.007 ms
总结:
- 在自定义网络中,可以给容器起多个别名,默认的别名是容器名本身
- 在同一个自定义网络中的容器,可以通过别名互相访问
3.项目部署
3.1手动部署
演示利用docker部署前后端项目
3.1.1部署后端项目
(1)将后端项目打包成jar包,编写好dockerfile
# 基础镜像
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 test-service.jar /app.jar
# 入口
ENTRYPOINT ["java", "-jar", "/app.jar"]
在项目中,通过访问数据库容器的名称来访问数据库:
(2)将jar包和dockerfile复制到服务器中:
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# ls
Dockerfile test-service.jar
(3)构建项目镜像
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker build -t projectimage .
[+] Building 3.3s (8/8) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 300B 0.0s
=> [internal] load metadata for docker.io/library/openjdk:11.0-jre-buster 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/3] FROM docker.io/library/openjdk:11.0-jre-buster 0.0s
=> [internal] load build context 1.3s
=> => transferring context: 68.24MB 1.2s
=> CACHED [2/3] RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo Asia/S 0.0s
=> [3/3] COPY test-service.jar /app.jar 1.3s
=> exporting to image 0.4s
=> => exporting layers 0.4s
=> => writing image sha256:b37378571ae8f8ba3d81ff730628655e4d685d23a304e1c603e7e9a8831f8e97 0.0s
=> => naming to docker.io/library/projectimage 0.0s
(4)创建镜像对应的容器并启动
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker run -d --name testproject -p 8080:8080 --network mynet projectimage
16d873208430edd13e7bb52a9ac5cd64d8a06a399e07f8418342bd8898b9a54d
之前已经在Docker中部署过mysql01容器,当项目容器和数据库容器mysql01在同一网桥中,便可以自行连接到数据库中。
测试访问接口:
- 总结:
- 项目打jar包,上传到服务器
- 利用dockerfile,构建项目的镜像
- 用docker run命令创建运行容器
3.1.2部署前端项目
创建一个新的nginx容器,将自定义的nginx.conf、html目录和容器挂载
nginx.conf:
worker_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;
#testproject为之前部署的后端项目容器名称
proxy_pass http://testproject: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;
#testproject为之前部署的后端项目容器名称
proxy_pass http://testproject:8080;
}
}
}
(1)将文件上传到服务器中
[root@iZ7xvgwzig5m0o0rnf19bkZ nginx]# ls
html nginx.conf
[root@iZ7xvgwzig5m0o0rnf19bkZ nginx]# pwd
/root/nginx
(2)创建nginx容器,并将上述两个文件挂载到nginx容器中
[root@iZ7xvgwzig5m0o0rnf19bkZ nginx]# docker run -d \
> --name nginx01 \
> -p 18080:18080 \
> -p 18081:18081 \
> -v /root/nginx/html:/usr/share/nginx/html \
> -v /root/nginx/nginx.conf:/etc/nginx/nginx.conf \
> --network mynet \
> nginx
6dc5fa9fa1925fd7869fd9bcfad08ac1f7b891e61f197cd1451d3539a284d0ed
mynet是之前创建的网桥
测试访问项目:
总结:
- 准备静态资源html目录和nginx.conf
- 创建容器,进行挂载
需要注意的是,nginx和Java后端项目,后端和数据之间都是使用容器名称来访问的,并且这三个容器都要在同一个网络中
3.2Docker Compose
DockerCompose通过一个单独的docke-compose.yml模板文件(YAML格式)来定义一组相关联的应用容器,帮助我们实现多个相互关联的Docker容器的快速部署。
3.2.1基本语法
docker-compose.yml文件的基本语法可以参考官方文档:
Compose file version 3 reference | Docker Docs
3.2.2基础命令
docker compose的基本语法如下:
docker compose [OPTIONS] [COMMAND]
其中,OPTIONS和COMMAND都是可选参数,比较常见的有:
- 使用DockerCompose演示之前的项目部署
DockerCompose文件:
version: "3.8"
services:
mysql: #服务名称
image: mysql:8.0.20 #镜像,若本地没有,Compose将尝试从互联网拉取这个镜像
container_name: mysql01 #容器名
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: #网络设置
- my-net
javaproject:
build: #每个服务都必须通过image指令指定镜像,或build指令(需要 Dockerfile)等来自动构建生成镜像。
context: .
dockerfile: Dockerfile
container_name: testproject
ports:
- "8080:8080"
networks: #加入哪个网络中
- my-net
depends_on: #依赖于哪个服务
- mysql
nginx:
image: nginx
container_name: nginx01
ports:
- "18080:18080"
- "18081:18081"
volumes:
- "./nginx/nginx.conf:/etc/nginx/nginx.conf"
- "./nginx/html:/usr/share/nginx/html"
depends_on:
- javaproject
networks:
- my-net
networks:
my-net: #这里是网络的标识
name: mynet #网络的名字
ex
(1)将之前的容器、自定义的镜像、网络全部删除
[root@iZ7xvgwzig5m0o0rnf19bkZ demo]# docker rm -f nginx01 testproject mysql01
nginx01
testproject
mysql01
[root@iZ7xvgwzig5m0o0rnf19bkZ demo]# docker rmi projectimage mydockerimage
Untagged: projectimage:latest
Deleted: sha256:b37378571ae8f8ba3d81ff730628655e4d685d23a304e1c603e7e9a8831f8e97
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker network rm mynet
mynet
(2)将DockerCompose文件上传到对应的文件路径下,并使用DockerCompose命令一键启动部署项目
注意:dockercompose文件中需要用到的目录和文件等,需要事先创建和放到对应的路径中
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker compose up -d
[+] Building 0.1s (8/8) FINISHED docker:default
=> [javaproject internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 300B 0.0s
=> [javaproject internal] load metadata for docker.io/library/openjdk:11.0-jre-buster 0.0s
=> [javaproject internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [javaproject 1/3] FROM docker.io/library/openjdk:11.0-jre-buster 0.0s
=> [javaproject internal] load build context 0.0s
=> => transferring context: 40B 0.0s
=> CACHED [javaproject 2/3] RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && 0.0s
=> CACHED [javaproject 3/3] COPY test-service.jar /app.jar 0.0s
=> [javaproject] exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:6b312ff9212db83ffed789f8680aaf8e088c1ae23de2ba8faa7dcc99bb93e342 0.0s
=> => naming to docker.io/library/root-javaproject 0.0s
[+] Running 3/4
⠧ Network mynet Created 2.7s
✔ Container mysql01 Started 0.5s
✔ Container testproject Started 1.0s
✔ Container nginx01 Started 2.2s
查看项目下的所有进程:
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
mysql01 mysql:8.0.20 "docker-entrypoint.s…" mysql About a minute ago Up About a minute 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp
nginx01 nginx "/docker-entrypoint.…" nginx About a minute ago Up About a minute 80/tcp, 0.0.0.0:18080-18081->18080-18081/tcp, :::18080-18081->18080-18081/tcp
testproject root-javaproject "java -jar /app.jar" javaproject About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp
在浏览器访问项目成功,说明所有服务都成功部署:
使用docker compose的删除命令,整个项目创建的所有服务容器和网络都可以一键删除:
[root@iZ7xvgwzig5m0o0rnf19bkZ ~]# docker compose down
[+] Running 4/4
? Container nginx01 Removed 0.3s
? Container testproject Removed 0.4s
? Container mysql01 Removed 1.9s
? Network mynet Removed 0.2s
DockerCompose的功能远不止如此,它还可以用来做集群的部署。