显示导航

使用 Docker 为你的 Grails 应用程序提供外部服务

了解如何在使用 Docker 容器的 Grails 应用程序中使用 Postgresql 数据库

作者:Iván López、Sergio del Amo

Grails 版本 3.3.2

1 培训

Grails 培训 - 由创建和积极维护 Grails 框架的人员开发和提供!。

2 开始

如果你从事任何真正的 Grails 开发,则需要连接到外部服务;数据库、ElasticSearch、Redis 等服务。

通常,你需要在你的开发机器上安装这些服务,以复制生产环境。你可能会遇到这样的情况:在 MacOS 中开发一个连接到 MSSQL 数据库的 Grails 应用程序。在你的开发环境中安装这样的外部服务可能会很困难,甚至不可能。

在许多其他事情中,Docker 可以帮助我们简化此类场景。在本指南中,你将编写一个连接到运行在 Docker 容器中的 PostgreSQL 数据库的 Grails 应用程序。你不会在你的机器上安装 Postgres。相反,你将提取一个 PostgreSQL 映像,使用该映像创建一个容器,并启动该容器。你将修改你的 Grails 应用程序以连接到容器中运行的 PostgreSQL 数据库。

2.1 如何完成该指南

要开始,请执行以下操作

Grails 指南代码库包含两个文件夹

  • initial 初始项目。通常是一个简单的 Grails 应用程序,其中包含一些额外代码,让你可以抢先一步。

  • complete 一个已完成的示例。它是按照指南介绍的步骤操作并对 initial 文件夹应用这些更改的结果。

要完成指南,请进入 initial 文件夹

  • grails-guides/grails-docker-external-services/initial 中执行 cd

并按照后续章节中的说明操作。

如果您在 grails-guides/grails-docker-external-services/complete 中执行 cd,则可以直接转到 已完成的示例

3 编写指南

3.1 安装和配置 Docker

如果您尚未安装 Docker,则需要 对其进行安装

根据您的环境,您可能需要将 Docker 的可用内存增加到 4GB 或更多。

Docker Preferences

提示:获取 Kitematic

Kitematic 是一个用于管理 Docker 容器的优秀的桌面应用程序。首次单击 Open Kitematic 时,它会提示您下载并安装它。然后,您可以使用 Kitematic 查看容器的输出、管理其设置等。

Kitematic

3.2 域类

创建一个域类。它将帮助您验证在 docker 容器内运行的 PostgreSQL 数据库中创建的数据库架构。

grails-app/domain/demo/Book.groovy
package demo

import groovy.transform.CompileStatic

@CompileStatic
class Book {
    String title
}

3.3 Gradle Docker 插件

在本指南中,我们使用 Gradle Docker 插件;一个用于管理 Docker 镜像和容器的 Gradle 插件。

要安装 Gradle 插件,请修改 build.gradle

build.gradle
buildscript {
    repositories {
        mavenLocal()
        maven { url "https://repo.grails.org/grails/core" }
    }
    dependencies {
        classpath "org.grails:grails-gradle-plugin:$grailsVersion"
        classpath "gradle.plugin.com.energizedwork.webdriver-binaries:webdriver-binaries-gradle-plugin:1.1"
        classpath "gradle.plugin.com.energizedwork:idea-gradle-plugins:1.4"
        classpath "org.grails.plugins:hibernate5:${gormVersion-".RELEASE"}"
        classpath "com.bertramlabs.plugins:asset-pipeline-gradle:2.14.6"
        classpath "com.bmuschko:gradle-docker-plugin:3.2.1" (1)
    }
}
apply plugin:"eclipse"
apply plugin:"idea"
apply plugin:"war"
apply plugin:"org.grails.grails-web"
apply plugin:"com.energizedwork.webdriver-binaries"
apply plugin:"com.energizedwork.idea-project-components"
apply plugin:"asset-pipeline"
apply plugin:"org.grails.grails-gsp"
apply plugin:"com.bmuschko.docker-remote-api" (1)
1 Gradle Docker 插件

3.4 Gradle Docker 任务

配置若干 Gradle 任务,这些任务允许我们获取一个 Docker 镜像,并使用 Gradle 创建/启动/停止一个 PostgreSQL 容器

build.gradle
import com.bmuschko.gradle.docker.tasks.container.DockerCreateContainer
import com.bmuschko.gradle.docker.tasks.container.DockerStartContainer
import com.bmuschko.gradle.docker.tasks.container.DockerStopContainer
import com.bmuschko.gradle.docker.tasks.image.DockerPullImage
...
..
.
task pullPostgresImage(type: DockerPullImage) {
    group = 'docker' (1)
    ext {
        imageName = 'postgres' (2)
        imageTag = '9.6.6'  (3)
    }
    description = 'Pull PostgreSQL image'
    repository = imageName
    tag = imageTag
}
task createPostgresContainer(type: DockerCreateContainer, dependsOn: pullPostgresImage) {
    group = 'docker'  (1)
    ext {
        pgContainerName = "demo-postgres" (4)
        dbName = "demo_db" (5)
        dbPort = 5432 (6)
        dbPassword = 'dev_password'  (7)
    }
    description = 'Creates PostgreSQL container'
    containerName = pgContainerName
    imageId = "${pullPostgresImage.imageName}:${pullPostgresImage.tag}"
    portBindings = ["${dbPort}:5432"]
    env = [
        "POSTGRES_PASSWORD=${dbPassword}",
        "POSTGRES_DB=${dbName}",
    ] as String[]

    onError { e ->
        if (e.class.simpleName in ['BadRequestException', 'ConflictException']) {
            logger.warn 'Container already exists' (8)
        } else {
            throw e
        }
    }
}
task startPostgresContainer(type: DockerStartContainer, dependsOn: createPostgresContainer) {
    group = 'docker' (1)
    description = 'Starts Postgres container'
    containerId = createPostgresContainer.pgContainerName
    onError { e ->
        if (e.class.simpleName == 'NotModifiedException') {
            logger.warn 'Container already started'  (8)
        } else {
            throw e
        }
    }
    onComplete {
        logger.info "Postgres is listening on port ${createPostgresContainer.dbPort}"
    }
}
task stopPostgresContainer(type: DockerStopContainer) {
    group = 'docker' (1)
    description = 'Stops Postgres container'
    containerId = createPostgresContainer.pgContainerName
    onError { e ->
        if (e.class.simpleName == 'NotModifiedException') {
            logger.warn 'Container already stopped' (8)
        } else {
            throw e
        }
    }
}
1 在组中对相关任务进行分组。当您运行 gradlew tasks --all 时,它有助于您可视化 Gradle 任务
2 Docker 镜像名称
3 Docker 镜像标签
4 Docker 容器名称
5 数据库名称
6 数据库端口
7 数据库用户的密码。默认情况下,用户名为 postgres
8 如果容器已创建、已启动或已停止,并且调用了 Gradle 任务,则不失败

3.5 配置 PostgreSQL

修改 build.gradle 并添加 PostgreSQL 依赖项

build.gradle
provided "org.postgresql:postgresql:9.4.1211.jre7"

将开发环境的 dataSource 配置为使用在 Docker 中运行的 PostgreSQL 数据库。

grails-app/conf/application.yml
dataSource:
    dialect: org.hibernate.dialect.PostgreSQLDialect
    driverClassName: org.postgresql.Driver
    username: postgres
    password: dev_password
    dbCreate: update
    url: jdbc:postgresql://localhost:5432/demo_db

4 运行应用程序

在运行应用程序之前,首先启动 PostgreSQL

./gradlew startPostgresContainer

然后启动 Grails 应用程序

./gradlew bootRun

如果您连接到数据库,则会看到预期的数据库架构。

Database created

bootRun 依赖于 PostgreSQL

您可以将 bootRun 任务配置为依赖于 startPostgresContainer 任务,在 build.gradle 中添加以下内容

bootRun.dependsOn startPostgresContainer

这将创建以下依赖关系路径

bootRun → startPostgresContainer → createPostgresContainer → pullPostgresImage

这种路径可能对持续集成环境感兴趣。

5 仅使用 Docker

还有一种可选项不需要使用 Gradle 插件。通过这种方法,我们只需使用普通 Docker来构建 Docker 映像并启动容器。

定义 Docker 文件

创建以下 Dockerfile

src/main/docker/Dockerfile
FROM postgres:9.6.6 (1)

VOLUME /var/lib/postgresql/data (2)

COPY ["setup.sh", "/docker-entrypoint-initdb.d/"] (3)

EXPOSE 5432 (4)
1 使用与之前相同的 PostgreSQL 映像和版本
2 为存储数据定义卷
3 将脚本复制到入口目录。启动容器时会执行此目录中的任何 .sh.sql 文件
4 公开端口

还要创建文件 setup.sh

src/main/docker/setup.sh
#!/bin/bash
echo "######### CREATING DATABASE ##########"

# Perform all actions as user 'postgres'
export PGUSER=postgres

# Create specific users for the application and also the databases for dev and test
psql <<EOSQL
    (1)
    CREATE DATABASE dev_demo_db;
    CREATE ROLE dev_user WITH LOGIN PASSWORD 'dev_password';
    GRANT ALL PRIVILEGES ON DATABASE dev_demo_db TO dev_user;

    (2)
    CREATE DATABASE test_demo_db;
    CREATE ROLE test_user WITH LOGIN PASSWORD 'test_password';
    GRANT ALL PRIVILEGES ON DATABASE test_demo_db TO test_user;
EOSQL

echo ""
echo "######### DATABASE CREATED ##########"
1 为开发环境创建数据库、用户和密码
2 为测试环境定义另一个数据库、用户和密码

构建映像

创建了这些文件后,我们来构建 docker 映像

$ docker build -t postgres-custom src/main/docker/

启动容器

基于我们刚刚创建的映像创建并启动容器(首次)

$ docker run -d -p 5432:5432 --name demo-postgres postgres-custom

从现在开始,我们每次想要启动/停止容器时,只需要执行

$ docker start demo-postgres

$ docker stop demo-postgres
请记住,您还需要更改 application.yml 以使用 setup.sh 文件中定义的新凭证。

将数据库开发环境配置替换为

grails-app/conf/application.yml
environments:
    development:
        dataSource:
            dialect: org.hibernate.dialect.PostgreSQLDialect
            driverClassName: org.postgresql.Driver
            username: dev_user
            password: dev_password
            dbCreate: update
            url: jdbc:postgresql://localhost:5432/dev_demo_db

6 获得有关 Grails 的帮助

对象计算公司 (OCI) 赞助了本指南的创建。各种咨询和支持服务现已推出。

OCI 是 Grails 的家园

认识团队