在开发中使用Docker

引言

随着微服务和云原生概念的盛行,未来的服务端架构将越来越多得将项目构建为Docker镜像来进行部署。在开发和集成构建阶段使用Docker成为服务端开发人员的必须技能。

本文主要关注在开发阶段使用Docker,所以不包括容器编排,集群网络,负载均衡等分布式相关的内容。有关分布式主题相关内容可以通过学习kubernetes来理解。

安装Docker Desktop for Mac

在mac中安装docker可以通过homebrew进行安装

1
2
3
brew cask install docker
## 验证安装是否成功
docker --help

也可以到参照文档进行手动下载安装

示例项目准备

本文以SpringFox-Plus这个项目的示例模块为例,该项目使用kotlin开发,提供Swagger-API接口文档的WEB服务。

  1. 下载并构建该项目

    1
    2
    3
    git clone https://github.com/hadix-lin/springfox-plus.git
    cd springfox-plus/springfox-plus-sample
    mvn package -P with-api-doc
  2. 准备Docker镜像

    1
    2
    3
    4
    5
    echo $(pwd) # 接第一步中的命令,当前workdir应该为springfox-plus-sample
    mkdir docker-image
    cp target/springfox-plus-sample-1.0.1.jar docker-image/sample.jar
    cd docker-image
    vim Dockerfile

    编辑Dockerfile文件内容如下:

    1
    2
    3
    4
    5
    FROM openjdk:8
    COPY . /app
    WORKDIR /app
    EXPOSE 8080/tcp
    CMD ["java","-jar","sample.jar"]

    开始构建镜像

    1
    docker build -t springfox-plus:sample .

运行

前面的操作已经准备好了Docker镜像,下面开始让应用运行起来

1
2
3
4
5
6
7
8
9
docker run  # 运行指令
-d\ # 后台运行(detached)
-p 8080:8080\ # 绑定主机和容器的端口
--rm\ # 容器退出后自动清除容器
--name sample # 指定容器名称
springfox-plus:sample # 要运行的镜像

# 将命令写在单行
docker run -d -p 8080:8080 --rm --name sample springfox-plus:sample

然后使用浏览器访问地址[http://127.0.0.1:8080/swagger-ui.html](http://127.0.0.1:8080/swagger-ui.html]即可访问swagger-ui,如下图所示:

image-20190514180551496

改进开发流程

上文的构建->运行的流程比较适合将发布到生产环境的时候使用,在开发阶段,我们会经常进行修改测试,如果每次都要重新构建镜像然后运行,就会比较繁琐。我们期望在开发阶段只构建一次镜像,该镜像保持和生产一直的配置,但是应用包本身不需要包含在其中,这样更改应用包就不需要重新构建镜像了。

可以通过VOLUME来将让容器直接访问本地的应用,下面开发修改Dockerfile为如下内容:

1
2
3
4
5
FROM openjdk:8
RUN mkdir /app
WORKDIR /app
EXPOSE 8080/TCP
CMD ["java","-jar","springfox-plus-sample-1.0.1.jar"]

使用上面的Dockerfile构建一个新的Docker镜像,然后使用—mount参数来运行

1
2
3
4
5
6
7
8
9
# workdir = docker-image
docker build -t springfox-plus:sample-use-mount .
cd ..
# workdir = springfox-plus-sample
docker run -d -p 8080:8080 --name sample-use-mount --mount \
type=bind,source="$(pwd)"/target,target=/app,readonly \
springfox-plus:sample-use-mount
# 停止容器运行
docker stop sample-use-mount

这样每次更改应用包只要使用mvn package重新打包,然后使用上面的命令重启容器即可,避免了重新构建镜像。

如果使用IntellijIDEA,那么可以通过Run Configuration进行配置:

image-20190514194653182

可以直接将mvn package配置到Before launch的列表中,这样每次使用这个Run Configuration运行docker容器时都会重新构建应用包,可以进一步减少开发过程中的人工操作。

调试

应用运行在Docker容器中,对开发者来说可以视为运行在另外远程服务器上,所以可以通过远程调试端口来进行调试。

只需要更改Dockerfile中的CMD,在入口命令加入java远程调试的相关设置即可。例如:

1
2
3
4
5
6
FROM openjdk:8
COPY . /app
WORKDIR /app
EXPOSE 8080/tcp
CMD ["java","-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005",\
"-jar","sample.jar"]

重新构建镜像并运行,即可通过端口5005进行远程调试了。

在IntellijIDEA中配置远程调试,如图:

image-20190514195906722

访问运行的容器

在上文中运行容器我们使用的命令是docker run -d …… 其中的-d参数表示后台运行容器,此时我们看不到容器中运行的应用的标准输入/输出。

使用docker attach <container-name>可以将当前终端的标准输入/输出/错误流关联到指定的容器上。执行该命令后,就可以在当前终端中看到容器的控制台输出了。

如果想在容器中运行命令可以使用docker exec [OPTIONS] CONTAINER COMMAND [ARG…],进一步可以利用该命令获得与运行的容器进行交互的能力。

如果容器运行的镜像中包含bash命令,可以使用docker exec -it CONTAINER bash来在容器中运行bash,并将当前终端的标准输入/输出/错误流关联到容器。这样就可以通过命令行跟运行的容器进行交互了,在这个命令行中能够使用的命令取决于运行的镜像内容。

有关docker命令的具体使用可以使用docker help COMMAND来查看使用说明,可以参考文档

命令总结

1
2
3
4
5
6
7
8
9
10
11
# 验证docker安装,输出使用说明
docker --help
# 构建镜像
docker build -t <tag> <context_path>
# 启动容器
docker run -d -p <container_port>:<host_port> --name <container_name> \
--mount <mount_params> <image:tag>
# 停止容器
docker stop <container_name or id>
# 将标准输入、输出、错误流关联到运行的容器
docker attach <container_name or id>

后续内容在开发中使用Docker-续.md