如何在Docker映像中安装grails? [英] How do I install grails in a Docker image?

查看:63
本文介绍了如何在Docker映像中安装grails?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在容器化基于grails构建的现有应用程序.我已经能够使用 gradle在 debian:stretch 图像(是的,我知道那很旧,但是该项目最初是使用几乎所有内容的旧版本)中成功构建的. maven default-jdk 已安装.但是我不确定这是否意味着我不需要grails.当我从容器内的命令行提示符尝试 grails --version 时,它会显示"bash:grails:not found".

I'm working on containerizing an existing application built on grails. I've been able to successfully build the project within a debian:stretch image (yes, I know that's old, but the project was originally built with old versions of just about everything) with gradle, maven, and default-jdk installed. However I'm unsure if that means I don't need grails. When I try grails --version from a command line prompt within the container it says "bash: grails: command not found".

似乎没有可以使用 apt 安装的 grails 单独的软件包,所有参考文献似乎都说是通过 sdkman 安装grails的.代码>.

There does not appear to be a separate package for grails that I can install using apt and all references seem to say to install grails via sdkman.

我完全不熟悉grails或groovy,所以我不确定如何进行.关于如何安装grails的任何建议?如果需要的话,没有它,构建是如何成功的?

I'm not at all familiar with grails or groovy so I'm unsure of how to proceed. Any advice on how to install (or maybe verify installation?) of grails? If it's needed, how did the build succeed without it?

推荐答案

这是我们在环境中使用的:

This is what we use in our environments:

docker-compose.yml

version: '3.7'

services:
  db:
    image: postgres:12.3
    environment:
      - POSTGRES_DB=databasename
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    ports:
      - 5432:5432
    networks:
      - mynetwork

  backend:
    build:
      context: .
      target: development
    ports:
      - 8080:8080
      - 8083:8443
      - 5006:5005
    environment:
      - DB_HOST=db
      - DB_NAME=databasename
      - DB_PORT=5432
      - DB_USERNAME=postgres
      - DB_PASSWORD=postgres
    volumes:
      - grails-volume:/root/.m2
      - grails-volume:/root/.gradle
      - grails-volume:/root/.grails
      - ./grails-app:/app/grails-app
      - ./src:/app/src
      - ./version.txt:/version.txt
    entrypoint: ["sh", "-c", "./wait-for-it.sh db:5432 -t 30 -- grails run-app"]
    command: [""]
    depends_on:
      - db
    networks:
      - mynetwork

volumes:
  grails-volume:

networks:
  mynetwork:

Dockerfile:

# Image to start project and initialize dependencies.
FROM openjdk:8 AS initializer
ENV GRAILS_VERSION 4.0.3
# Install Grails
WORKDIR /usr/lib/jvm
RUN ls -l
RUN wget https://github.com/grails/grails-core/releases/download/v$GRAILS_VERSION/grails-$GRAILS_VERSION.zip && \
    unzip grails-$GRAILS_VERSION.zip && \
    rm -rf grails-$GRAILS_VERSION.zip && \
    ln -s grails-$GRAILS_VERSION grails
# Setup Grails path.
ENV GRAILS_HOME /usr/lib/jvm/grails
ENV PATH $GRAILS_HOME/bin:$PATH
ENV GRADLE_USER_HOME /app/.gradle
# Create minimal structure to trigger grails build with specified profile.
RUN mkdir /app \
    && mkdir /app/grails-app \
    && mkdir /app/grails-app/conf \
    && echo "grails.profile: rest-api" > /app/grails-app/conf/application.yml
# Set Workdir
WORKDIR /app
# Copy minimun files to trigger grails download of wrapper and dependencies.
COPY gradle.properties build.gradle /app/
# Trigger gradle build
RUN [ "grails", "stats" ]

# Implemented to improve cache in CI
FROM initializer as development
# Add wait-for-it ro wait for database
COPY wait-for-it.sh .
RUN ["chmod", "+x", "./wait-for-it.sh"]
# Copy source code
COPY grails-app /app/grails-app
COPY src /app/src
# Set Default Behavior
ENTRYPOINT ["./wait-for-it.sh", "db:5432", "-t", "30", "--", "grails", "run-app", "--debug-jvm"]
CMD [ "" ]

# Image used to build prod war
FROM development AS builder
# Build project
RUN [ "grails", "prod", "war" ]
RUN ls -l /app/build/libs

# Production image
FROM openjdk:8-jdk AS production
# Set correct timezone
ENV TZ=America/Argentina/Cordoba
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# Add wait-for-it ro wait for database
COPY wait-for-it.sh .
RUN ["chmod", "+x", "/wait-for-it.sh"]
# Copy war inside container
COPY --from=builder /app/build/libs/app-*.war app.war
# Expose default port
EXPOSE 8080
# Wait for database to be available
ENTRYPOINT ["/wait-for-it.sh", "db-service:5432", "-t", "30", "--"]
# War runs directly. (Uses urandom as entropy source for faster startup time)
CMD ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.war"]

wait-for-it.sh:

#!/usr/bin/env bash
#   Use this script to test if a given TCP host/port are available

WAITFORIT_cmdname=${0##*/}

echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }

usage()
{
    cat << USAGE >&2
Usage:
    $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]
    -h HOST | --host=HOST       Host or IP under test
    -p PORT | --port=PORT       TCP port under test
                                Alternatively, you specify the host and port as host:port
    -s | --strict               Only execute subcommand if the test succeeds
    -q | --quiet                Don't output any status messages
    -t TIMEOUT | --timeout=TIMEOUT
                                Timeout in seconds, zero for no timeout
    -- COMMAND ARGS             Execute command with args after the test finishes
USAGE
    exit 1
}

wait_for()
{
    if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
        echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
    else
        echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout"
    fi
    WAITFORIT_start_ts=$(date +%s)
    while :
    do
        if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
            nc -z $WAITFORIT_HOST $WAITFORIT_PORT
            WAITFORIT_result=$?
        else
            (echo > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
            WAITFORIT_result=$?
        fi
        if [[ $WAITFORIT_result -eq 0 ]]; then
            WAITFORIT_end_ts=$(date +%s)
            echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds"
            break
        fi
        sleep 1
    done
    return $WAITFORIT_result
}

wait_for_wrapper()
{
    # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
    if [[ $WAITFORIT_QUIET -eq 1 ]]; then
        timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
    else
        timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
    fi
    WAITFORIT_PID=$!
    trap "kill -INT -$WAITFORIT_PID" INT
    wait $WAITFORIT_PID
    WAITFORIT_RESULT=$?
    if [[ $WAITFORIT_RESULT -ne 0 ]]; then
        echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
    fi
    return $WAITFORIT_RESULT
}

# process arguments
while [[ $# -gt 0 ]]
do
    case "$1" in
        *:* )
        WAITFORIT_hostport=(${1//:/ })
        WAITFORIT_HOST=${WAITFORIT_hostport[0]}
        WAITFORIT_PORT=${WAITFORIT_hostport[1]}
        shift 1
        ;;
        --child)
        WAITFORIT_CHILD=1
        shift 1
        ;;
        -q | --quiet)
        WAITFORIT_QUIET=1
        shift 1
        ;;
        -s | --strict)
        WAITFORIT_STRICT=1
        shift 1
        ;;
        -h)
        WAITFORIT_HOST="$2"
        if [[ $WAITFORIT_HOST == "" ]]; then break; fi
        shift 2
        ;;
        --host=*)
        WAITFORIT_HOST="${1#*=}"
        shift 1
        ;;
        -p)
        WAITFORIT_PORT="$2"
        if [[ $WAITFORIT_PORT == "" ]]; then break; fi
        shift 2
        ;;
        --port=*)
        WAITFORIT_PORT="${1#*=}"
        shift 1
        ;;
        -t)
        WAITFORIT_TIMEOUT="$2"
        if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi
        shift 2
        ;;
        --timeout=*)
        WAITFORIT_TIMEOUT="${1#*=}"
        shift 1
        ;;
        --)
        shift
        WAITFORIT_CLI=("$@")
        break
        ;;
        --help)
        usage
        ;;
        *)
        echoerr "Unknown argument: $1"
        usage
        ;;
    esac
done

if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then
    echoerr "Error: you need to provide a host and port to test."
    usage
fi

WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}
WAITFORIT_STRICT=${WAITFORIT_STRICT:-0}
WAITFORIT_CHILD=${WAITFORIT_CHILD:-0}
WAITFORIT_QUIET=${WAITFORIT_QUIET:-0}

# check to see if timeout is from busybox?
WAITFORIT_TIMEOUT_PATH=$(type -p timeout)
WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)
if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then
        WAITFORIT_ISBUSY=1
        WAITFORIT_BUSYTIMEFLAG="-t"

else
        WAITFORIT_ISBUSY=0
        WAITFORIT_BUSYTIMEFLAG=""
fi

if [[ $WAITFORIT_CHILD -gt 0 ]]; then
    wait_for
    WAITFORIT_RESULT=$?
    exit $WAITFORIT_RESULT
else
    if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
        wait_for_wrapper
        WAITFORIT_RESULT=$?
    else
        wait_for
        WAITFORIT_RESULT=$?
    fi
fi

if [[ $WAITFORIT_CLI != "" ]]; then
    if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
        echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
        exit $WAITFORIT_RESULT
    fi
    exec "${WAITFORIT_CLI[@]}"
else
    exit $WAITFORIT_RESULT
fi

application.yml

#...Omitted code...
environments:
    development:
        dataSource:
            driverClassName: org.postgresql.Driver
            dialect: org.hibernate.dialect.PostgreSQLDialect
            # dbCreate: create-drop
            dbCreate: update
            url: jdbc:postgresql://${DB_HOST}:${DB_PORT}/${DB_NAME}
    test:
        dataSource:
            dbCreate: update
            url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
    production:
        dataSource:
            driverClassName: org.postgresql.Driver
            dialect: org.hibernate.dialect.PostgreSQLDialect
            dbCreate: update
            url: jdbc:postgresql://${DB_HOST}:${DB_PORT}/${DB_NAME}

在开发中,您将使用docker-compose文件运行映像(请注意,它具有一些卷,以避免在更改应用程序时重新启动应用程序): docker-compose up -d backend

In development you would use the docker-compose file to run the image (note that it has some volumes in order to avoid the need to restart the app when you change it): docker-compose up -d backend

要生成用于生产的映像: docker build -t yourdockerhubuser/yourproject:versionnumber.

To generate an image for production: docker build -t yourdockerhubuser/yourproject:versionnumber .

不需要 wait-for-it.sh 文件来生成Docker映像.但是,这对于使grails服务等待几秒钟,直到数据库服务准备好接受连接是很有用的.

The wait-for-it.sh file is not needed to generate a Docker image. But it is useful to us in order to make the grails service to wait some seconds until the database service is ready to accept connections...

如果查看Dockerfile,您会注意到这是一个多阶段文件.这意味着它有几个阶段.第一个有grails工具.最后一个是用于生产的,只有jdk8(也许jre就足够了).这样一来,您就不会污染生产图像,从而不会产生较小的图像.

If you take a look at the Dockerfile you would note that is a multistage file. That means that has like several stages on it. The first ones has grails tools on it. The last one is the one used on production and has only the jdk8 (and probably the jre would be enough). That way you don't pollute the production image resulting in a smaller image.

这篇关于如何在Docker映像中安装grails?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆