From 383005bc8d05cfe6ece4eca3f1366710f4134f05 Mon Sep 17 00:00:00 2001 From: Eduardo Cabral <47820549+FerroEduardo@users.noreply.github.com> Date: Thu, 26 Oct 2023 12:54:59 -0300 Subject: [PATCH] feature: deploy containers Build using docker, publish to Docker Hub and run containers using Podman --- .github/workflows/deploy-prod.yaml | 80 ++++++++---------------------- Dockerfile | 41 +++++++++++++++ start-container.sh | 67 +++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 59 deletions(-) create mode 100644 Dockerfile create mode 100644 start-container.sh diff --git a/.github/workflows/deploy-prod.yaml b/.github/workflows/deploy-prod.yaml index 3f4b8c1..d129842 100644 --- a/.github/workflows/deploy-prod.yaml +++ b/.github/workflows/deploy-prod.yaml @@ -27,7 +27,23 @@ jobs: run: chmod +x gradlew - name: 'Build and Generate JAR' - run: ./gradlew deploy --no-daemon -P version=${{github.ref_name}} + run: ./gradlew build deploy --no-daemon -P version=${{github.ref_name}} + + - name: Log in to Docker Hub + uses: docker/login-action@v3.0.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5.0.0 + with: + context: . + file: ./Dockerfile + build-args: | + APP_VERSION=${{github.ref_name}} + push: ${{ github.event_name != 'pull_request' }} + tags: eduardoferro/capivara:${{ github.sha }},${{ github.event_name == 'release' && format('eduardoferro/capivara:latest,eduardoferro/capivara:{0}', github.ref_name) || '' }} - name: 'Wait for SSH' run: | @@ -46,70 +62,16 @@ jobs: env: INSTANCE_IP: ${{ secrets.INSTANCE_IP }} - - name: 'Stop and Delete Previous App' - uses: appleboy/ssh-action@v0.1.4 - continue-on-error: true - with: - host: ${{ secrets.INSTANCE_IP }} - username: ${{ secrets.SSH_USERNAME }} - passphrase: ${{ secrets.VM_SSH_PRIVATE_KEY_PASSPHRASE }} - key: ${{ secrets.VM_SSH_PRIVATE_KEY }} - script: | - pkill -f 'java .* -jar .*CapivaraBot.*\.jar' - rm -rf /home/${{ secrets.SSH_USERNAME }}/capivara/CapivaraBot.jar - - - name: 'Push Repo' + - name: 'Push start-container.sh' uses: appleboy/scp-action@v0.1.4 with: host: ${{ secrets.INSTANCE_IP }} username: ${{ secrets.SSH_USERNAME }} passphrase: ${{ secrets.VM_SSH_PRIVATE_KEY_PASSPHRASE }} key: ${{ secrets.VM_SSH_PRIVATE_KEY }} - source: "./build/libs/CapivaraBot.jar" + source: "./start-container.sh" target: /home/${{ secrets.SSH_USERNAME }}/capivara/ - strip_components: 3 - - - name: 'Create main.properties' - uses: appleboy/ssh-action@v0.1.4 - with: - host: ${{ secrets.INSTANCE_IP }} - username: ${{ secrets.SSH_USERNAME }} - passphrase: ${{ secrets.VM_SSH_PRIVATE_KEY_PASSPHRASE }} - key: ${{ secrets.VM_SSH_PRIVATE_KEY }} - script: | - cd /home/${{ secrets.SSH_USERNAME }}/capivara - - echo 'spring.datasource.url=jdbc:postgresql://localhost:5432/capivara' > main.properties - echo 'spring.datasource.driverClassName=org.postgresql.Driver' >> main.properties - echo 'spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL95Dialect' >> main.properties - echo 'spring.datasource.username=${{ secrets.DATABASE_USERNAME }}' >> main.properties - echo 'spring.datasource.password=${{ secrets.DATABASE_PASSWORD }}' >> main.properties - echo 'hibernate.hbm2ddl.auto=update' >> main.properties - echo 'token=${{ secrets.DISCORD_BOT_TOKEN }}' >> main.properties - echo 'log.channel.id=${{ secrets.DISCORD_LOG_CHANNEL_ID }}' >> main.properties - echo 'log.directory=/home/${{ secrets.SSH_USERNAME }}/capivara/logs/' >> main.properties - - - name: 'Create start.sh' - uses: appleboy/ssh-action@v0.1.4 - with: - host: ${{ secrets.INSTANCE_IP }} - username: ${{ secrets.SSH_USERNAME }} - passphrase: ${{ secrets.VM_SSH_PRIVATE_KEY_PASSPHRASE }} - key: ${{ secrets.VM_SSH_PRIVATE_KEY }} - script: | - cd /home/${{ secrets.SSH_USERNAME }}/capivara - - echo '#!/bin/bash' > start.sh - echo 'source "/home/${{ secrets.SSH_USERNAME }}/.sdkman/bin/sdkman-init.sh"' >> start.sh - - echo 'DIRECTORY=/home/${{ secrets.SSH_USERNAME }}/capivara' >> start.sh - echo 'FILENAME=CapivaraBot.jar' >> start.sh - echo 'FULLPATH=${DIRECTORY}/${FILENAME}' >> start.sh - echo 'JAVA_ARGS=-Xmx300M' >> start.sh - - echo 'cd $DIRECTORY' >> start.sh - - echo 'java $JAVA_ARGS -jar $FULLPATH --spring.config.location=file:/home/${{ secrets.SSH_USERNAME }}/capivara/main.properties --curupira.reset=${1-true}' >> start.sh + strip_components: 1 - name: 'Start BOT' uses: appleboy/ssh-action@v0.1.4 @@ -120,4 +82,4 @@ jobs: key: ${{ secrets.VM_SSH_PRIVATE_KEY }} script: | cd /home/${{ secrets.SSH_USERNAME }}/capivara - nohup bash start.sh > nohup.out 2> nohup.err < /dev/null & + DISCORD_TOKEN=${{ secrets.DISCORD_BOT_TOKEN }} LOG_CHANNEL_ID=${{ secrets.DISCORD_LOG_CHANNEL_ID }} CURUPIRA_RESET=true DATABASE_DRIVER=org.postgresql.Driver DATABASE_DIALECT=org.hibernate.dialect.PostgreSQL95Dialect DATABASE_URL=jdbc:postgresql://localhost:5432/capivara DATABASE_USERNAME=${{ secrets.DATABASE_USERNAME }} DATABASE_PASSWORD=${{ secrets.DATABASE_PASSWORD }} JAVA_ARGS=-Xmx200M /bin/bash start-container.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f9565d3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,41 @@ +FROM docker.io/eclipse-temurin:17-jdk-alpine as builder + +WORKDIR /app +ADD . /tmp + +ARG APP_VERSION="invalid" + +RUN cd /tmp && \ + chmod +x gradlew && \ + ./gradlew deploy -P version=$APP_VERSION --no-daemon && \ + mv build/libs/CapivaraBot.jar /app && \ + rm -rf /tmp/* + +FROM docker.io/eclipse-temurin:17-jre-alpine + +WORKDIR /app +COPY --from=builder /app . + +ENV JAVA_ARGS="-Xmx300M" +ENV LOG_DIRECTORY="/app/logs" +ENV SPRING_CONFIG_LOCATION="/app/main.properties" +ENV DISCORD_TOKEN="invalid" +ENV CURUPIRA_RESET="true" +ENV LOG_CHANNEL_ID="invalid" +ENV DATABASE_DRIVER="org.postgresql.Driver" +ENV DATABASE_DIALECT="org.hibernate.dialect.PostgreSQL95Dialect" +ENV DATABASE_URL="jdbc:postgresql://localhost:5432/capivara" +ENV DATABASE_USERNAME="database-username" +ENV DATABASE_PASSWORD="database-password" + +ENTRYPOINT java ${JAVA_ARGS} -jar CapivaraBot.jar \ + --token=${DISCORD_TOKEN} \ + --spring.config.location=file:${SPRING_CONFIG_LOCATION} \ + --curupira.reset=${CURUPIRA_RESET} \ + --log.directory=${LOG_DIRECTORY} \ + --log.channel.id=${LOG_CHANNEL_ID} \ + --spring.datasource.driverClassName=${DATABASE_DRIVER} \ + --spring.jpa.database-platform=${DATABASE_DIALECT} \ + --spring.datasource.url=${DATABASE_URL} \ + --spring.datasource.username=${DATABASE_URL} \ + --spring.datasource.password=${DATABASE_URL} \ No newline at end of file diff --git a/start-container.sh b/start-container.sh new file mode 100644 index 0000000..da98251 --- /dev/null +++ b/start-container.sh @@ -0,0 +1,67 @@ +if [ -z "${1}" ]; then + echo "Missing container repository and tag" + exit 1; +else + echo "Container repository and tag: $1" +fi +CONTAINER_REPOSITORY=${1} + +if [[ -z "${DISCORD_TOKEN}" ]] || [[ -z "${LOG_CHANNEL_ID}" ]] ; then + echo 'Missing required environment variable: DISCORD_TOKEN, LOG_CHANNEL_ID' + exit 1 +fi + +CONTAINER_NAME="capivara_bot" +OLD_CONTAINER_ID=$(podman ps --all --quiet --filter "name=$CONTAINER_NAME") + +if [[ -z "${OLD_CONTAINER_ID}" ]]; then + echo "Container not found" +else + echo "Found container $OLD_CONTAINER_ID" + echo "Stopping..." + WAIT=10 # seconds + podman stop $OLD_CONTAINER_ID -t $WAIT || { echo 'Failed to stop container'; exit 1; } + echo "Removing..." + podman rm --force $OLD_CONTAINER_ID || { echo 'Failed to remove container'; exit 1; } +fi + +echo "Starting container..." +podman run -d \ + -e DISCORD_TOKEN=$DISCORD_TOKEN \ + -e LOG_CHANNEL_ID=$LOG_CHANNEL_ID \ + -e CURUPIRA_RESET=${CURUPIRA_RESET:-false} \ + -e DATABASE_DRIVER=${DATABASE_DRIVER:-org.h2.Driver} \ + -e DATABASE_DIALECT=${DATABASE_DIALECT:-org.hibernate.dialect.H2Dialect} \ + -e DATABASE_URL=${DATABASE_URL:-jdbc:h2:file:./banco_h2;DB_CLOSE_DELAY=-1} \ + -e DATABASE_USERNAME=${DATABASE_USERNAME:-sa} \ + -e DATABASE_PASSWORD=${DATABASE_PASSWORD:-sa} \ + -e JAVA_ARGS=${JAVA_ARGS:--Xmx200M} \ + --cpus 0.5 \ + --memory 200M \ + --name $CONTAINER_NAME \ + --restart always \ + $CONTAINER_REPOSITORY || { echo 'Failed to start container failed'; exit 1; } + + +SLEEP=10 +CHECKS=0 +MAX_CHECKS=3 +echo "Starting 'healthcheck'" +while true; do + NEW_CONTAINER_ID=$(podman ps --quiet --filter "name=$CONTAINER_NAME") + if [[ -z "${NEW_CONTAINER_ID}" ]]; then + echo "Container died? exiting" + exit 1 + fi + + if [[ CHECKS -eq MAX_CHECKS ]]; then + break + fi + ((CHECKS += 1)) + sleep $SLEEP +done; + +echo "Container should be alive" + +echo "Cleaning images..." +podman image prune -a -f || { echo 'Failed to start clean images'; exit 1; }