docker官方文档解读

官网:https://docs.docker.com/get-started/overview/

1 Docker底层原理

1.1 docker使用的底层技术

首先,需要强调的一点是,目前的容器化技术基于Linux内核的两个重要特性:cgroups 和 Namespace

容器技术发展历史:https://blog.csdn.net/Tencent_TEG/article/details/109505143

容器的实质是进程,和宿主机上的其他进程是平级的。与宿主机上的其他进程是共用一个内核,但与直接在宿主机执行的进程不同,容器进程运行在属于自己的独立的命名空间。命名空间隔离了进程间的资源,使得 a,b 进程可以看到 S 资源,而 c 进程看不到。有关Namespace和cgroups的介绍可参考:https://www.cnblogs.com/aozhejin/p/16183046.html

1.2 

2 开始

2.1 创建镜像并启动

这里我们以一个nodejs用于为例,说明容器化应用程序过程

首先克隆这个应用程序源码

 git clone https://github.com/docker/getting-started.git 

然后,创建一个Dockerfile文件

[root@localhost app]# pwd
/data/srcs/getting-started/app
[root@localhost app]# vi Dockerfile

Dockerfile内容为

# syntax=docker/dockerfile:1

FROM node:18-alpine
WORKDIR /app
COPY . .
RUN yarn install --production --registry=https://registry.npm.taobao.org
# 如果yarn install命令执行失败,可以改为npm install试一下 CMD ["node", "src/index.js"] EXPOSE 3000

构建镜像

 docker build -t getting-started . 

其中-t参数为镜像打一个tag,.参数表示在当前路径下查找Dockerfile文件

启动镜像

 docker run -dp 3000:3000 getting-started 

然后我们访问宿主机的3000端口,可以看到应用程序部署成功

有时候我们重新生成了镜像,需要重新部署镜像

[root@localhost app]# docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                                       NAMES
c77251691610   05a7b6b9d2f4   "docker-entrypoint.s…"   37 minutes ago   Up 37 minutes   0.0.0.0:3000->3000/tcp, :::3000->3000/tcp   ecstatic_cartwright
[root@localhost app]# docker stop c77251691610
c77251691610
[root@localhost app]# docker rm c77251691610
c77251691610
[root@localhost app]# docker run -dp 3000:3000 getting-started
89ed2658b10f65f80d616fe571de53e2fae9113ff05da125eec9371b19f82eef
[root@localhost app]#

2.2 共享镜像

共享镜像的意思是,我们将生成的镜像push到远程仓库。

我们以推送到docker hub为例

首先,需要在docker hub注册账号。

然后,登陆docker hub

 docker login -u zhenjingcool 

然后,执行docker tag命令

 docker tag getting-started zhenjingcool/getting-started 

然后执行docker push命令

 docker push zhenjingcool/getting-started 

然后,我们会发现仓库中多了一个镜像

 接下来,我们在play with docker中运行刚才上传的镜像

首先登陆play with docker,然后点击"ADD NEW INSTANCE",然后执行 docker run -dp 3000:3000 zhenjingcool/getting-started ,如下图所示

然后,我们就可以看到我们运行的镜像应用了

2.3 使用volumes

上面的例子中,当我们重新启动getting started镜像后,我们之前在镜像实例中配置的todo list会丢失。这里我们使用volumes来进行数据的持久化。

我们的数据库在/etc/todos/todo-db文件中,我们创建一个volume然后挂载到容器的/etc/todos/路径下。

1 创建一个volume

[root@localhost ~]# docker volume create todo-db
todo-db
[root@localhost ~]# docker volume ls
DRIVER    VOLUME NAME
local     todo-db

2 启动容器

docker run -dp 3000:3000 --mount type=volume,src=todo-db,target=/etc/todos getting-started

然后我们可以在页面上增加几个todo项,然后重启容器,我们会发现,todo项做到了持久化。

3 查看volume详情

[root@localhost ~]# docker volume inspect todo-db
[
    {
        "CreatedAt": "2023-04-19T05:44:13-07:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/todo-db/_data",
        "Name": "todo-db",
        "Options": null,
        "Scope": "local"
    }
]
[root@localhost ~]#

这里,Mountpoint指的是磁盘上数据的实际位置。

2.4 使用bind mounts

上面我们使用volume方式挂载到容器中的特定路径来实现数据的持久化。这一小节我们使用另外一种方式,即bind mounts实现宿主机路径挂载到容器特定路径下,来实现数据持久化。

bind mounts是另一种挂载方式,这种方式允许我们共享宿主机的一个目录。

下面是volumes和bind mounts的简单对比

 Named volumesBind mounts
Host locationDocker choosesYou decide
Mount example (using --mount)type=volume,src=my-volume,target=/usr/local/datatype=bind,src=/path/to/data,target=/usr/local/data
Populates new volume with container contentsYesNo
Supports Volume DriversYesNo

实例

docker run -it --mount type=bind,src="$(pwd)",target=/src ubuntu bash

我们查看一下容器的/src路径,发现挂载了宿主机的/data/目录

root@08807d48a248:/src# ll
total 28
drwxr-xr-x. 3 root root    68 Apr 17 14:46 ./
drwxr-xr-x. 1 root root    17 Apr 19 13:53 ../
-rw-r--r--. 1 root root 26024 Apr 24  2019 mysql80-community-release-el7-3.noarch.rpm
drwxr-xr-x. 3 root root    29 Apr 17 14:46 srcs/
root@08807d48a248:/src#

我们在容器中创建/src/test.txt,然后退出容器,发现,在宿主机上也能看到创建的这个文件。

root@08807d48a248:/src# touch test.txt
root@08807d48a248:/src# ll
total 28
drwxr-xr-x. 3 root root    84 Apr 19 13:54 ./
drwxr-xr-x. 1 root root    17 Apr 19 13:53 ../
-rw-r--r--. 1 root root 26024 Apr 24  2019 mysql80-community-release-el7-3.noarch.rpm
drwxr-xr-x. 3 root root    29 Apr 17 14:46 srcs/
-rw-r--r--. 1 root root     0 Apr 19 13:54 test.txt
root@08807d48a248:/src# exit
exit
[root@localhost data]# ll
total 28
-rw-r--r--. 1 root root 26024 Apr 24  2019 mysql80-community-release-el7-3.noarch.rpm
drwxr-xr-x. 3 root root    29 Apr 17 07:46 srcs
-rw-r--r--. 1 root root     0 Apr 19 06:54 test.txt
[root@localhost data]#

2.5 多容器应用

一般情况下我们会把一个应用分立为几个容器。原因是:

  • 方便扩展
  • 分立容器可以独立更新,互不影响

这里我们的应用有两个容器部署,一个运行mysql,另一个运行应用本身

各个容器之前采用网络连接的方式进行通信。

2.5.1 部署mysql容器

1 创建network

docker network create todo-app

2 启动容器

docker run -d \
     --network todo-app --network-alias mysql \
     -v todo-mysql-data:/var/lib/mysql \
     -e MYSQL_ROOT_PASSWORD=secret \
     -e MYSQL_DATABASE=todos \
     mysql:8.0

其中-v用于指定volumes挂载,其中volume名称为todo-mysql-data,不存在将会自动创建,挂载到容器中的/var/lib/mysql。

-e指定环境变量

我们登陆mysql看看是否成功创建数据库实例

[root@localhost app]# docker exec -it f21178ba297f mysql -u root -p

我们可以看到,数据库实例成功创建了

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| todos              |
+--------------------+
5 rows in set (0.00 sec)

mysql>
2.5.2 使用netshoot进行网络诊断

一般,我们部署容器时,都是尽量减小容器体积,所以,容器中很多命令是无法使用的。比如netstat,ll等命令。

在这里,我们需要知道上面部署的mysql容器的ip,以便我们可以访问数据库。

我们使用nicolaka/netshoot进行网络诊断,在netshoot中,安装了很多和网络诊断相关的命令。

1 启动一个nicolaka/netshoot容器

docker run -it --network todo-app nicolaka/netshoot

启动成功后,我们看到如下界面

 上面,我们启动mysql容器的时候,设置了容器的网络别名为mysql,这里,我们将使用 dig mysql 命令来解析这个网络,获取它的ip地址,如上图所示。

因此,我们得到的mysql容器的ip地址为172.18.0.2

2.5.3 启动应用容器

1 启动容器

首先,我们要进入到源码路径下 /data/srcs/getting-started/app/ 

在源码路径下的package.json中,有如下脚本

  "scripts": {
    "prettify": "prettier -l --write \"**/*.js\"",
    "test": "jest",
    "dev": "nodemon src/index.js"
  },

因此,我们执行yarn run dev命令后执行的是 nodemon src/index.js 

nodemon是一个nodejs代码调试工具,我们在源码路径下改动了代码之后,容器将会自动重新部署。

在当前路径下执行如下命令启动容器

docker run -dp 3000:3000 \
   -w /app -v "$(pwd):/app" \
   --network todo-app \
   -e MYSQL_HOST=mysql \
   -e MYSQL_USER=root \
   -e MYSQL_PASSWORD=secret \
   -e MYSQL_DB=todos \
   node:18-alpine \
   sh -c "yarn install && yarn run dev"

其中,这里我们启动一个node运行环境,且-w指定工作目录,-v指定使用volume,--network指定网络,-e指定环境变量,sh -c指定容器启动后要执行的sh命令。

2 查看启动日志

我们看到,我们成功连接上了mysql数据库

[root@localhost app]# docker logs --details 4f274e0723b8
 yarn install v1.22.19
 [1/4] Resolving packages...
 [2/4] Fetching packages...
 info There appears to be trouble with your network connection. Retrying...
 [3/4] Linking dependencies...
 [4/4] Building fresh packages...
 Done in 219.40s.
 yarn run v1.22.19
 $ nodemon src/index.js
 [nodemon] 2.0.20
 [nodemon] to restart at any time, enter `rs`
 [nodemon] watching path(s): *.*
 [nodemon] watching extensions: js,mjs,json
 [nodemon] starting `node src/index.js`
 Waiting for mysql:3306.
 Connected!
 Connected to mysql db at host mysql
 Listening on port 3000
[root@localhost app]#

3 页面添加todos,查看效果

当上面docker启动日志打印出Listening on port 3000后,我们在浏览器中访问应用,并添加两个待办项,然后去mysql中查看是否插入数据库中了

mysql> select * from todo_items;
+--------------------------------------+----------------+-----------+
| id                                   | name           | completed |
+--------------------------------------+----------------+-----------+
| f41a2fe4-b9ef-44f5-97c3-1a405419a8b2 | 待办项添加到m-01 |         0 |
| 26aeb4e6-c373-43b0-9117-25b7023c6c80 | 待办项添加到m-02 |         0 |
+--------------------------------------+----------------+-----------+
2 rows in set (0.00 sec)

mysql>

成功。

2.6 Docker Compose

Docker Compose是一个用来帮助定义多容器应用的工具。

查看Docker Compose版本

[root@localhost ~]# docker compose version
Docker Compose version v2.17.2
[root@localhost ~]#

我们以getting-started应用为例,说明Docker Compose的使用方法,我们要配置两个镜像,一个应用本身的镜像,另一个mysql的镜像,我们把需要配置的镜像写到下面

docker run -dp 3000:3000 \
  -w /app -v "$(pwd):/app" \
  --network todo-app \
  -e MYSQL_HOST=mysql \
  -e MYSQL_USER=root \
  -e MYSQL_PASSWORD=secret \
  -e MYSQL_DB=todos \
  node:18-alpine \
  sh -c "yarn install && yarn run dev"

docker run -d \
  --network todo-app --network-alias mysql \
  -v todo-mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  -e MYSQL_DATABASE=todos \
  mysql:8.0

我们使用docker compose来达到上述两个命令相同的结果

首先,进入到项目源码根路径下,创建 docker-compose.yml 文件,其内容为

services:
  app:
    image: node:18-alpine
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      MYSQL_DB: todos

  mysql:
    image: mysql:8.0
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: todos

volumes:
  todo-mysql-data:

然后我们使用命令 docker compose up -d 启动

[root@localhost app]# pwd
/data/srcs/getting-started/app
[root@localhost app]# docker compose up -d
[+] Running 4/4
 ✔ Network app_default           Created                                                                                                                                                                 0.4s
 ✔ Volume "app_todo-mysql-data"  Created                                                                                                                                                                 0.0s
 ✔ Container app-mysql-1         Started                                                                                                                                                                 5.4s
 ✔ Container app-app-1           Started                                                                                                                                                                 5.6s
[root@localhost app]#

我们可以看一下启动日志

[root@localhost app]# docker compose logs -f
...
app-app-1    | yarn install v1.22.19
app-app-1    | [1/4] Resolving packages...
app-app-1    | success Already up-to-date.
app-app-1    | Done in 1.31s.
app-app-1    | yarn run v1.22.19
app-app-1    | $ nodemon src/index.js
app-app-1    | [nodemon] 2.0.20
app-app-1    | [nodemon] to restart at any time, enter `rs`
app-app-1    | [nodemon] watching path(s): *.*
app-app-1    | [nodemon] watching extensions: js,mjs,json
app-app-1    | [nodemon] starting `node src/index.js`
app-app-1    | Waiting for mysql:3306.
app-app-1    | Connected!
app-app-1    | Connected to mysql db at host mysql
app-app-1    | Listening on port 3000

然后我们打开浏览器访问应用,能够成功访问,说明启动成功了

我们使用 docker compose down 命令来关闭Docker Compose启动的所有容器。

[root@localhost app]# docker compose down
[+] Running 3/3
 ✔ Container app-mysql-1  Removed                                                                                                                                                                        3.4s
 ✔ Container app-app-1    Removed                                                                                                                                                                        0.4s
 ✔ Network app_default    Removed                                                                                                                                                                        0.3s
[root@localhost app]#

2.7 Image Layering镜像分层

镜像分层,意思是一个镜像是由很多层组成。我们使用如下命令进行查看

docker image history getting-started

然后其输出是这样的

[root@localhost ~]# docker image history getting-started:latest
IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
b5e92c3a7fce   3 days ago    EXPOSE map[3000/tcp:{}]                         0B        buildkit.dockerfile.v0
<missing>      3 days ago    CMD ["node" "src/index.js"]                     0B        buildkit.dockerfile.v0
<missing>      3 days ago    RUN /bin/sh -c npm install --production --re…   50.5MB    buildkit.dockerfile.v0
<missing>      3 days ago    COPY . . # buildkit                             4.59MB    buildkit.dockerfile.v0
<missing>      4 days ago    WORKDIR /app                                    0B        buildkit.dockerfile.v0
<missing>      8 days ago    /bin/sh -c #(nop)  CMD ["node"]                 0B
<missing>      8 days ago    /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<missing>      8 days ago    /bin/sh -c #(nop) COPY file:4d192565a7220e13…   388B
<missing>      8 days ago    /bin/sh -c apk add --no-cache --virtual .bui…   7.78MB
<missing>      8 days ago    /bin/sh -c #(nop)  ENV YARN_VERSION=1.22.19     0B
<missing>      8 days ago    /bin/sh -c addgroup -g 1000 node     && addu…   160MB
<missing>      8 days ago    /bin/sh -c #(nop)  ENV NODE_VERSION=18.16.0     0B
<missing>      3 weeks ago   /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>      3 weeks ago   /bin/sh -c #(nop) ADD file:9a4f77dfaba7fd2aa…   7.05MB

我们对照Dockerfile查看

[root@localhost ~]# cat /data/srcs/getting-started/app/Dockerfile
# syntax=docker/dockerfile:1

FROM node:18-alpine
WORKDIR /app
COPY . .
RUN npm install --production --registry=https://registry.npm.taobao.org
CMD ["node", "src/index.js"]
EXPOSE 3000

他们是一一对应的。

2.8 镜像分层原理

参考https://blog.csdn.net/weixin_37926734/article/details/123267870

镜像是一个轻量级、可执行的独立软件包,它包含了运行软件所需要的所有内容

镜像中在每一层上只记录本层所做的更改,而且这些层是只读层。当启动一个容器,Docker会在最顶部添加读写层,在容器内作的所有更改(写日志、修改、删除文件等,都保存到读写层内),一般称该层为容器层,如下图所示。

容器需要读取某个文件时,直接从底部只读层去读即可,而如果需要修改某文件,则将该文件拷贝到顶部读写层进行修改,只读层保持不变。

docker容器底层使用的是linux的容器化技术:namespace,在宿主机看来,容器内的进程和普通进程并没有差别,他们共享同一个内核,共同参与cpu时间片调度。

然而,他们其实是有差别的,他们的差别就是所在的namespace不同,对于一个容器,内核使用的是宿主机内核,但是发行版使用的是自己的发行版。普通的发行版镜像动辄几个G大小,但是容器不需要这个大而全的发行版,容器对发行版进行了裁剪,只保留运行应用最基础的软件和依赖,往往只有200M就够了。

3 Docker Desktop

docker desktop是一个工具,它提供了GUI界面,我们通过docker desktop可以方便的管理镜像和容器。

windows下安装docker desktop需要开启虚拟化,win10默认是开启的,可以通过任务管理器-CPU查看是否开启虚拟化。此外,还需要在启用和关闭windows功能中启用Hyper-V。具体安装过程不再细说。

安装后的界面是这样的

建议配置一下国内的镜像,这样下载速度会快很多。

3.1 简单例子

上面我们安装了docker desktop之后,我们在powershell中启动一个容器

docker run -p 8088:80 -d --name welcome-to-docker docker/welcome-to-docker

然后我们在Docker Desktop中查看,可以看到我们运行中的容器,如上图所示。

然后我们可以在浏览器中打开这个容器应用。

并且,通过Docker Desktop,我们可以查看系统日志、容器内部的文件、打开终端等功能

 我们也可以T通过Docker Desktop关闭容器

3.2 制作镜像并通过Docker Desktop运行

下载源码

git clone https://github.com/docker/welcome-to-docker

构建镜像

PS E:\code\welcome-to-docker> docker build -t welcome-to-docker .
[+] Building 326.7s (11/11) FINISHED
 => [internal] load build definition from Dockerfile                                                               0.5s
 => => transferring dockerfile: 32B                                                                                0.0s
 => [internal] load .dockerignore                                                                                  0.7s
 => => transferring context: 34B                                                                                   0.0s
 => [internal] load metadata for docker.io/library/node:18-alpine                                                 18.7s
 => [internal] load build context                                                                                  0.4s
 => => transferring context: 393B                                                                                  0.0s
 => [1/6] FROM docker.io/library/node:18-alpine@sha256:ca5d399560a9d239cbfa28eec00417f1505e5e108f3ec6938d230767ea  0.0s
 => CACHED [2/6] WORKDIR /app                                                                                      0.0s
 => CACHED [3/6] COPY package*.json ./                                                                             0.0s
 => CACHED [4/6] COPY ./src ./src                                                                                  0.0s
 => CACHED [5/6] COPY ./public ./public                                                                            0.0s
 => [6/6] RUN npm install     && npm install -g serve     && npm run build     && rm -fr node_modules            302.3s
 => exporting to image                                                                                             3.8s
 => => exporting layers                                                                                            3.2s
 => => writing image sha256:95a572fa36a516f602db7a408350baecf21380b07067590c2e51d649de9dd0ae                       0.1s
 => => naming to docker.io/library/welcome-to-docker                                                               0.0s
PS E:\code\welcome-to-docker>

Desktop中启动

然后我们会看到容器启动日志

 然后我们访问localhost:8089将会看到应用运行了

3.3 运行Docker Hub镜像

Docker desktop中有一个搜索框,可以搜索Docker Hub的镜像,并直接启动。

此外,我们也可以通过Docker Desktop发布自己的镜像到Docker Hub仓库。

4 java相关

4.1 构建镜像

这里,我们以一个springboot项目为例,说明如何把一个项目构建成镜像

1 下载源码

我们进入到E:\code\路径下,执行如下命令

git clone https://github.com/spring-projects/spring-petclinic.git

然后进入到E:\code\spring-petclinic路径下,创建两个文件Dockfile文件和.dockerignore文件

Dockfile

# syntax=docker/dockerfile:1

FROM eclipse-temurin:17-jdk-focal

WORKDIR /app

COPY .mvn/ .mvn
COPY mvnw pom.xml ./
RUN ./mvnw dependency:resolve

COPY src ./src

CMD ["./mvnw", "spring-boot:run"]

.dockerignore

target

2 构建镜像

docker build --tag java-docker .

3 查看镜像

docker images
REPOSITORY          TAG                 IMAGE ID            CREATED          SIZE
java-docker         latest              b1b5f29f74f0        47 minutes ago   567MB

4 给镜像打Tag

docker tag java-docker:latest java-docker:v1.0.0

然后我们再次查看镜像

docker images
REPOSITORY    TAG      IMAGE ID          CREATED          SIZE
java-docker   latest   b1b5f29f74f0      59 minutes ago    567MB
java-docker   v1.0.0   b1b5f29f74f0      59 minutes ago    567MB

5 删除特定tag的镜像

docker rmi java-docker:v1.0.0
Untagged: java-docker:v1.0.0

然后我们再次查看镜像,发现v1.0.0的那个Tag被删除了

docker images
REPOSITORY      TAG     IMAGE ID        CREATED              SIZE
java-docker        latest    b1b5f29f74f0    59 minutes ago         567MB

4.2 启动容器

上面我们已经生成了镜像,现在我们启动运行,我们可以选择在Docker Desktop中启动,也可以选择在powershell中启动

docker run -p 8080:8080 java-docker

启动过程中,其实是执行的./mvnw springboot:run命令,会下载依赖包,会比较慢,大约会20分钟。

然后我们浏览器中访问http://localhost:8080/actuator/health验证服务是否正常。

4.3 多容器应用

这里我们构建两个镜像,一个运行mysql,另一个运行springboot。

1 创建两个volume

PS C:\Users\root> docker volume create mysql_data
mysql_data
PS C:\Users\root> docker volume create mysql_config
mysql_config
PS C:\Users\root> docker volume ls
DRIVER    VOLUME NAME
local     mysql_config
local     mysql_data

2 创建一个network

PS C:\Users\root> docker network create mysqlnet
843f80283398d4cc0cb3d12b22d6683e418118eb6dc7065441a676b50045b940
PS C:\Users\root>

3 构建并运行mysql镜像

docker run -it --rm -d -v mysql_data:/var/lib/mysql \
-v mysql_config:/etc/mysql/conf.d \
--network mysqlnet \
--name mysqlserver \
-e MYSQL_USER=petclinic -e MYSQL_PASSWORD=petclinic \
-e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=petclinic \
-p 3306:3306 mysql:8.0

其中--rm参数表示,如果有这个容器则删除旧的

4 修改Dockerfile文件

我们修改4.1小节中的那个java项目的Dockerfile文件,最后一行修改为如下内容。这里指定了profile变量。

CMD ["./mvnw", "spring-boot:run", "-Dspring-boot.run.profiles=mysql"]

指定了profile之后,springboot的配置文件将使用application-mysql.properties

# database init, supports mysql too
database=mysql
spring.datasource.url=${MYSQL_URL:jdbc:mysql://localhost/petclinic}
spring.datasource.username=${MYSQL_USER:petclinic}
spring.datasource.password=${MYSQL_PASS:petclinic}
# SQL is written to be idempotent so this is safe
spring.sql.init.mode=always

5 构建springboot镜像

docker build --tag java-docker .

6 启动springboot容器

docker run --rm -d \
--name springboot-server \
--network mysqlnet \
-e MYSQL_URL=jdbc:mysql://mysqlserver/petclinic \
-p 8080:8080 java-docker

因为我们指定了-d参数,因此,我们看不到启动过程(如果想看启动过程可以去Docker Desktop中查看),启动可能要一段时间。启动之后,我们访问一下链接看看效果

$ curl  --request GET --url http://localhost:8080/vets --header 'content-type: application/json'
{"vetList":[{"id":1,"firstName":"James","lastName":"Carter","specialties":[],"nrOfSpecialties":0,"new":false},{"id":2,"firstName":"Helen","lastName":"Leary","specialties":[{"id":1,"name":"radiology","new":false}],"nrOfSpecialties":1,"new":false},{"id":3,"firstName":"Linda","lastName":"Douglas","specialties":[{"id":3,"name":"dentistry","new":false},{"id":2,"name":"surgery","new":false}],"nrOfSpecialties":2,"new":false},{"id":4,"firstName":"Rafael","lastName":"Ortega","specialties":[{"id":2,"name":"surgery","new":false}],"nrOfSpecialties":1,"new":false},{"id":5,"firstName":"Henry","lastName":"Stevens","specialties":[{"id":1,"name":"radiology","new":false}],"nrOfSpecialties":1,"new":false},{"id":6,"firstName":"Sharon","lastName":"Jenkins","specialties":[],"nrOfSpecialties":0,"new":false}]}

4.4 多阶段Dockerfile

多阶段Dockerfile的意思是,在一个Dockerfile中,我们定义多个阶段,然后,我们启动一个镜像时,可以指定运行哪个阶段。

因为一个项目有开发和生产部署阶段,如果使用多阶段Dockerfile,我们可以在一个Dockerfile中定义多个阶段:开发环境、编译、生产。在开发阶段,我们只需要运行开发阶段,在生产部署时,我们运行容器时指定生产阶段就可以了,这样方便我们开发和部署。

例子,在这个Dockerfile中,我们定义了4个stage,分别是basedevelopmentbuildproduction。并且后一阶段可以用前一阶段的结果。

# syntax=docker/dockerfile:1

FROM eclipse-temurin:17-jdk-focal as base
WORKDIR /app
COPY .mvn/ .mvn
COPY mvnw pom.xml ./
RUN ./mvnw dependency:resolve
COPY src ./src

FROM base as development
CMD ["./mvnw", "spring-boot:run", "-Dspring-boot.run.profiles=mysql", "-Dspring-boot.run.jvmArguments='-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000'"]

FROM base as build
RUN ./mvnw package

FROM eclipse-temurin:17-jre-focal as production
EXPOSE 8080
COPY --from=build /app/target/spring-petclinic-*.jar /spring-petclinic.jar
CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/spring-petclinic.jar"]

4.5 使用Compose进行本地开发

在上一小节基础上,更进一步,我们在Dockerfile同级目录下创建docker-compose.dev.yml文件

version: '3.8'
services:
  petclinic:
    build:
      context: .
      target: development
    ports:
      - "8000:8000"
      - "8080:8080"
    environment:
      - SERVER_PORT=8080
      - MYSQL_URL=jdbc:mysql://mysqlserver/petclinic
    volumes:
      - ./:/app
    depends_on:
      - mysqlserver

  mysqlserver:
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=
      - MYSQL_ALLOW_EMPTY_PASSWORD=true
      - MYSQL_USER=petclinic
      - MYSQL_PASSWORD=petclinic
      - MYSQL_DATABASE=petclinic
    volumes:
      - mysql_data:/var/lib/mysql
      - mysql_config:/etc/mysql/conf.d
volumes:
  mysql_data:
  mysql_config:

然后我们通过docker-compose启动应用,这个过程会比较慢,大约10分钟左右会启动完毕

docker-compose -f docker-compose.dev.yml up --build

其中,--build表示重构镜像,在yml文件中,我们指定了build的阶段是development阶段,实际上会执行的命令是

CMD ["./mvnw", "spring-boot:run", "-Dspring-boot.run.profiles=mysql", "-Dspring-boot.run.jvmArguments='-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000'"]

然后我们访问一下看一下效果

$ curl  --request GET --url http://localhost:8080/vets --header 'content-type: application/json'
{"vetList":[{"id":1,"firstName":"James","lastName":"Carter","specialties":[],"nrOfSpecialties":0,"new":false},{"id":2,"firstName":"Helen","lastName":"Leary","specialties":[{"id":1,"name":"radiology","new":false}],"nrOfSpecialties":1,"new":false},{"id":3,"firstName":"Linda","lastName":"Douglas","specialties":[{"id":3,"name":"dentistry","new":false},{"id":2,"name":"surgery","new":false}],"nrOfSpecialties":2,"new":false},{"id":4,"firstName":"Rafael","lastName":"Ortega","specialties":[{"id":2,"name":"surgery","new":false}],"nrOfSpecialties":1,"new":false},{"id":5,"firstName":"Henry","lastName":"Stevens","specialties":[{"id":1,"name":"radiology","new":false}],"nrOfSpecialties":1,"new":false},{"id":6,"firstName":"Sharon","lastName":"Jenkins","specialties":[],"nrOfSpecialties":0,"new":false}]}

4.6 连接Debugger进行远程调试

我们使用idea打开这个项目,然后Run-Edit Configuration,添加RemoteDebug,如下

然后我们启动debug

然后我们在如下接口代码处打断点

    @GetMapping({ "/vets" })
    public @ResponseBody Vets showResourcesVetList() {
        // Here we are returning an object of type 'Vets' rather than a collection of Vet
        // objects so it is simpler for JSon/Object mapping
        Vets vets = new Vets();
        vets.getVetList().addAll(this.vetRepository.findAll());//断点打在此处
        return vets;
    }

然后我们调用接口

curl  --request GET --url http://localhost:8080/vets --header 'content-type: application/json'

我们会看到,远程调试起作用了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值