Skip to content

Commit 4644160

Browse files
committed
Add deployment workflow and update Dockerfile for application packaging
1 parent 8982fb1 commit 4644160

File tree

16 files changed

+583
-6
lines changed

16 files changed

+583
-6
lines changed

.github/workflows/deploy.yml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: Deploy to Server
2+
3+
on:
4+
push:
5+
branches:
6+
- develop
7+
workflow_run:
8+
workflows: [ "Playwright UI Tests (JUnit)" ]
9+
types:
10+
- completed
11+
12+
jobs:
13+
build-and-deploy:
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@v4
19+
20+
- name: Login to Docker Hub
21+
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
22+
23+
- name: Set up Temurin JDK 21
24+
uses: actions/setup-java@v4
25+
with:
26+
distribution: 'temurin'
27+
java-version: '21'
28+
cache: maven
29+
30+
- name: Verify Maven installation
31+
run: mvn -version
32+
33+
- name: Get product name and version
34+
run: |
35+
echo "VERSION=$(mvn help:evaluate -q -DforceStdout -D"expression=project.version")" >> $GITHUB_ENV
36+
echo "NAME=$(mvn help:evaluate -q -DforceStdout -D"expression=project.name")" >> $GITHUB_ENV
37+
38+
- name: Build Java project with Maven
39+
run: |
40+
mvn dependency:go-offline -Pproduction
41+
mvn clean package -DskipTests -Pproduction
42+
43+
- name: Build and push Docker image
44+
run: |
45+
docker build -t fredpena/${NAME}:lastest .
46+
docker push fredpena/${NAME}:lastest
47+
48+
- name: SSH into the server and deploy
49+
uses: appleboy/[email protected]
50+
with:
51+
host: ${{ secrets.SERVER_HOST }}
52+
username: ${{ secrets.SERVER_USER }}
53+
key: ${{ secrets.SSH_PRIVATE_KEY }}
54+
port: 22
55+
script: |
56+
pwd
57+
cd /usr/local/app/dev
58+
docker compose pull
59+
docker compose down -v
60+
docker compose up -d

.github/workflows/playwright-java.yml renamed to .github/workflows/playwright.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@ jobs:
2525
- name: Verify Maven installation
2626
run: mvn -version
2727

28-
- name: Build project (skip tests)
29-
run: |
30-
mvn dependency:go-offline -Pproduction
31-
mvn clean package -DskipTests -Pproduction
32-
3328
- name: Install Playwright CLI (Java)
3429
run: mvn exec:exec -Dexec.executable="npx" -Dexec.args="playwright install --with-deps"
3530

Dockerfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
FROM eclipse-temurin:21-jre
1+
FROM eclipse-temurin:21-jre
2+
COPY target/vaadin-playwright-junit.jar app.jar
3+
EXPOSE 40301
4+
ENTRYPOINT ["java", "-jar", "/app.jar"]

IaC/.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
FINGERPRINT=.....

IaC/Pulumi.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name: pulumi-iac
2+
description: JConf2025
3+
runtime: java
4+
config:
5+
pulumi:tags:
6+
value:
7+
pulumi:template: java
8+
jug: Jconf Dom 2025

IaC/README.md

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
# Infrastructure as Code (IaC) Project with Pulumi and Ansible
2+
3+
This project uses Pulumi to create cloud infrastructure on DigitalOcean and Ansible to configure and deploy
4+
applications. We’ll guide you through setting up a Pulumi project, deploying infrastructure, and using Ansible to
5+
install Docker and launch your application on a cloud server.
6+
7+
## Prerequisites
8+
9+
- Pulumi Account and CLI
10+
- Ansible
11+
- DigitalOcean API Token
12+
- SSH key added to DigitalOcean for access to the server
13+
14+
## Step 1: Infrastructure Setup with Pulumi
15+
16+
1. Create a Pulumi Project
17+
18+
```shell
19+
mkdir pulumi-iac
20+
```
21+
22+
```shell
23+
cd pulumi-iac
24+
```
25+
26+
```shell
27+
pulumi new java
28+
```
29+
30+
Note: You’ll need a Pulumi account and API token.
31+
Follow [these instructions](https://www.pulumi.com/docs/pulumi-cloud/access-management/access-tokens/) to generate your
32+
Pulumi access token.
33+
34+
2. Verify Pulumi Stack
35+
36+
Check that the stack is set up correctly:
37+
38+
```shell
39+
pulumi stack ls
40+
```
41+
42+
3. Deploy the Pulumi Stack
43+
44+
Deploy the infrastructure stack:
45+
46+
```shell
47+
pulumi up
48+
```
49+
50+
4. dd Pulumi DigitalOcean Dependency
51+
52+
Include the following dependency in your pom.xml to interact with DigitalOcean:
53+
54+
```xml
55+
56+
<dependency>
57+
<groupId>com.pulumi</groupId>
58+
<artifactId>digitalocean</artifactId>
59+
<version>4.33.0</version>
60+
</dependency>
61+
```
62+
63+
5. Create Resources with Pulumi
64+
65+
> Here’s an example code snippet to create a DigitalOcean Droplet:
66+
67+
```java
68+
import com.pulumi.Pulumi;
69+
import com.pulumi.digitalocean.Droplet;
70+
import com.pulumi.digitalocean.DropletArgs;
71+
import io.github.cdimascio.dotenv.Dotenv;
72+
73+
public class App {
74+
75+
public static void main(String[] args) {
76+
Dotenv dotenv = Dotenv.load();
77+
String fingerprint = dotenv.get("FINGERPRINT");
78+
79+
Pulumi.run(ctx -> {
80+
// Create a new Droplet in nyc1
81+
Droplet droplet = new Droplet("web", DropletArgs.builder()
82+
.image("debian-12-x64")
83+
.name("iac-jconf-2025")
84+
.region("nyc1")
85+
.size("s-1vcpu-2gb")
86+
.sshKeys(fingerprint)
87+
.build());
88+
89+
System.out.println("The public IP address of your Droplet application");
90+
System.out.println("IP => " + droplet.ipv4Address());
91+
});
92+
}
93+
}
94+
```
95+
96+
6. Add the Droplet IP to Ansible Hosts
97+
98+
Once created, add the droplet's IP to the hosts file in the main iac project folder. Locate `app` in the hosts file and
99+
replace it with your new droplet IP.
100+
101+
7. Generate a DigitalOcean Access Token
102+
103+
You’ll need a DigitalOcean access token to connect
104+
Ansible. [Generate a token here](https://docs.digitalocean.com/reference/api/create-personal-access-token) and save it
105+
as an environment variable:
106+
107+
```shell
108+
export DIGITALOCEAN_ACCESS_TOKEN=<your_token>
109+
```
110+
111+
8. ensure SSH Configuration
112+
113+
Make sure your SSH key registered with DigitalOcean matches the one set in `all:vars` in your Ansible hosts file:
114+
115+
```shell
116+
ansible_ssh_private_key_file=~/.ssh/id_rsa
117+
```
118+
119+
## Step 2: Configure Docker Deployment with Ansible
120+
121+
1. Update `docker-compose.yml.j2` for Production
122+
123+
Ensure your `docker-compose.yml.j2` template is ready for a production cloud environment. Point your domain to the IP
124+
generated by DigitalOcean, as Traefik may temporarily block access if domain settings aren’t correct.
125+
126+
Example configuration:
127+
128+
```yaml
129+
services:
130+
traefik:
131+
image: "traefik:v3.2"
132+
container_name: "traefik"
133+
command:
134+
- "--api.insecure=true"
135+
- "--providers.docker=true"
136+
- "--providers.docker.exposedbydefault=false"
137+
- "--entrypoints.websecure.address=:443"
138+
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
139+
140+
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
141+
- "--entrypoints.web.address=:80"
142+
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
143+
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
144+
- "--entrypoints.web.http.redirections.entrypoint.permanent=true"
145+
ports:
146+
- "80:80"
147+
- "443:443"
148+
- "8080:8080"
149+
volumes:
150+
- "./letsencrypt:/letsencrypt"
151+
- "/var/run/docker.sock:/var/run/docker.sock:ro"
152+
153+
whoami:
154+
image: traefik/whoami
155+
command:
156+
- --port=2001
157+
- --name=iamfoo
158+
labels:
159+
- "traefik.enable=true"
160+
- "traefik.http.routers.fredpena-b.rule=Host(`balancer.fredpena.dev`)"
161+
- "traefik.http.routers.fredpena-b.service=fredpena-b-service"
162+
- "traefik.http.routers.fredpena-b.entrypoints=websecure"
163+
- "traefik.http.services.fredpena-b-service.loadbalancer.server.port=2001"
164+
- "traefik.http.routers.fredpena-b.tls.certresolver=letsencrypt"
165+
- "traefik.http.middlewares.onlyhttps.redirectscheme.scheme=https"
166+
- "traefik.http.middlewares.onlyhttps.redirectscheme.permanent=true"
167+
168+
ssdwa:
169+
image: fredpena/ssdwa:1.0.0
170+
labels:
171+
- "traefik.enable=true"
172+
- "traefik.http.routers.fredpena.rule=Host(`app.fredpena.dev`)"
173+
- "traefik.http.routers.fredpena.service=fredpena-service"
174+
- "traefik.http.routers.fredpena.entrypoints=websecure"
175+
- "traefik.http.services.fredpena-service.loadbalancer.server.port=37650"
176+
- "traefik.http.routers.fredpena.tls.certresolver=letsencrypt"
177+
- "traefik.http.middlewares.onlyhttps.redirectscheme.scheme=https"
178+
- "traefik.http.middlewares.onlyhttps.redirectscheme.permanent=true"
179+
```
180+
181+
2. Run `install-docker.playbook.yml` to Set Up Docker
182+
183+
```shell
184+
ansible-playbook -i host install-docker.playbook.ym
185+
```
186+
187+
Execute the Ansible playbook to install Docker on the server.
188+
189+
```yaml
190+
---
191+
- hosts: app
192+
tasks:
193+
- name: Removing default Docker packages
194+
apt:
195+
pkg:
196+
- docker
197+
- docker-engine
198+
- docker.io
199+
- containerd
200+
- runc
201+
state: absent
202+
203+
- name: Installing required packages
204+
apt:
205+
pkg:
206+
- ca-certificates
207+
- curl
208+
- gnupg
209+
- lsb-release
210+
211+
- name: Create required directory
212+
file:
213+
path: /etc/apt/keyrings
214+
state: directory
215+
216+
- name: Adding Docker's official GPG key
217+
shell: curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --batch --yes --dearmor -o /etc/apt/keyrings/docker.gpg
218+
219+
- name: Setting Docker repository
220+
shell: |
221+
echo \
222+
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
223+
$(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list
224+
225+
- name: Installing Docker
226+
apt:
227+
pkg:
228+
- docker-ce
229+
- docker-ce-cli
230+
- containerd.io
231+
- docker-compose-plugin
232+
- python3-docker
233+
update-cache: yes
234+
```
235+
236+
3. Run `install-app.playbook.yml` to Deploy the Application
237+
238+
```shell
239+
ansible-playbook -i host install-app.playbook.yml
240+
```
241+
242+
After Docker is installed, use this playbook to deploy the application.
243+
244+
## Useful Commands
245+
246+
```shell
247+
pulumi destroy -s ...
248+
```
249+
250+
```shell
251+
pulumi stack rm ...
252+
```

IaC/ansible/ansible.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[defaults]
2+
inventory=hosts
3+
interpreter_python=/usr/bin/python3
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
services:
2+
traefik:
3+
image: "traefik:v3.4"
4+
container_name: "traefik"
5+
command:
6+
#- "--log.level=DEBUG"
7+
- "--api.insecure=true"
8+
- "--providers.docker=true"
9+
- "--providers.docker.exposedbydefault=false"
10+
- "--entrypoints.websecure.address=:443"
11+
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
12+
13+
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
14+
- "--entrypoints.web.address=:80"
15+
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
16+
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
17+
- "--entrypoints.web.http.redirections.entrypoint.permanent=true"
18+
ports:
19+
- "80:80"
20+
- "443:443"
21+
- "8080:8080"
22+
volumes:
23+
- "./letsencrypt:/letsencrypt"
24+
- "/var/run/docker.sock:/var/run/docker.sock:ro"
25+
26+
app:
27+
image: fredpena/vaadin-playwright-junit:latest
28+
labels:
29+
- "traefik.enable=true"
30+
- "traefik.http.routers.fredpena.rule=Host(`app.fredpena.dev`)"
31+
- "traefik.http.routers.fredpena.service=fredpena-service"
32+
- "traefik.http.routers.fredpena.entrypoints=websecure"
33+
- "traefik.http.services.fredpena-service.loadbalancer.server.port=40301"
34+
- "traefik.http.routers.fredpena.tls.certresolver=letsencrypt"
35+
36+
volumes:
37+
db_postgres:

0 commit comments

Comments
 (0)