Docker入门学习

Docker学习笔记

1.快速入门

准备:linux服务器or虚拟机,OS--Centos7。

安装Docker - 飞书云文档 (feishu.cn) 或者

Linux安装Docker完整教程-腾讯云开发者社区-腾讯云 (tencent.com)

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/confusr/share/nginx/html,但在容器内修改非常麻烦。有了数据卷,我们就可以将数据卷和容器内的目录进行挂载。

步骤如下:

  1. 首先使用docker命令创建两个数据卷 html 和 conf 。

    docker 会自动帮我们在宿主机文件系统中创建两个真实的目录 /var/lib/docker/volumes/html/_data/var/lib/docker/volumes/conf/_data。数据卷html会映射到真实目录下的 .../html/_data ,conf 则映射到真实目录下的 .../conf/_data。每一个数据卷都和宿主机上的一个目录进行一一对应。

  2. 创建数据卷后,让容器目录跟数据卷做挂载。即将容器的目录和数据卷进行关联,这样一来容器的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的网络功能来解决这个问题:

官方文档:docker network | Docker Docs

加入自定义网络的容器才可以通过容器名相互访问,不需要对方的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在同一网桥中,便可以自行连接到数据库中。

测试访问接口:

  • 总结:
  1. 项目打jar包,上传到服务器
  2. 利用dockerfile,构建项目的镜像
  3. 用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是之前创建的网桥

测试访问项目:

总结:

  1. 准备静态资源html目录和nginx.conf
  2. 创建容器,进行挂载

需要注意的是,nginx和Java后端项目,后端和数据之间都是使用容器名称来访问的,并且这三个容器都要在同一个网络中

3.2Docker Compose

DockerCompose通过一个单独的docke-compose.yml模板文件(YAML格式)来定义一组相关联的应用容器,帮助我们实现多个相互关联的Docker容器的快速部署。

3.2.1基本语法

docker-compose.yml文件的基本语法可以参考官方文档:

Compose file version 3 reference | Docker Docs

https://yeasy.gitbook.io/docker_practice/compose/commands

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的功能远不止如此,它还可以用来做集群的部署。

热门相关:影帝偏要住我家   铁血大明   虎狼之师   懒散初唐   上古传人在都市