- Published on
Run Spring Boot ด้วย Docker Compose
ขอบคุณรูปภาพจาก https://kyan-2015.s3.eu-west-1.amazonaws.com
แนะนำสำหรับคนที่เข้าใจและเคยใช้ Docker มาบ้างแล้ว
Spring boot service ผมจะไม่สร้างใหม่ แต่ผมจะใช้อันเดียวกับบทความนี้ Authentication ด้วย Spring Security & JWT เอามา Implement ต่อ Source Code
Docker Compose คืออะไร ?
เป็น Plugin อีกตัวของ Docker ที่อยู่ในรูปแบบของ Script เพื่อมาช่วยในการสร้าง Container และ Config หลายๆอันขึ้นมาพร้อมกัน โดยใช้คำสั่งเดียว docker-compose up
ผมจะไม่ขออธิบายลงรายละเอียดเพราะมี Blogger ท่านอื่นๆ ได้เขียนบทความอธิบายไว้ค่อนข้างดีมากๆแล้ว ผมแนะนำบทความนี้ [Docker EP.011] ทำความรู้จัก Docker compose ของ Natthakarn Pruksapong
Require
Implement
สร้างไฟล์ docker-compose.yml
version: '3'
services:
app:
image: api_backend
build: .
container_name: app
depends_on:
- db
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/postgres
- SPRING_DATASOURCE_USERNAME=postgres
- SPRING_DATASOURCE_PASSWORD=p@stgres
- SPRING_JPA_HIBERNATE_DDL_AUTO=update
ports:
- 8008:8080
db:
image: postgres:10.4
container_name: db
volumes:
- /var/db/data/postgresql:/var/lib/postgresql
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=p@stgres
ports:
- 5432:5432
- version: เลือกได้ว่าจะใช้ service เวอร์ชันไหน
- services: เราจะมีกี่ service ก็ได้ อย่างของผมมี 2 services app, db
- app
image: api_backend
ผมใช้ image ชื่อ api_backendbuild: .
เป็นคำสั่งในการ Build โดย.
คือบอกว่า Dockerfile อยู่ใน Directory เดียวกัน ถ้าเราไม่สั่งให้มัน Build เราจะต้อง Run คำสั่งdocker build -t api_backend .
เพื่อสร้าง Image ที่ชื่อapi_backend
ขึ้นมาก่อนcontainer_name: app
ตั้งชื่อ container ว่า appdepends_on:
คือ จะรัน container นี้ได้ก็ต่อเมื่อมี container ชื่อ dbenvironment
คือ กำหนดค่า config ต่างๆให้กับตัวแปรตาม environment คล้ายกับไฟล์ .envports
โดยที่ Host จะเปิด port TCP 8008 ไปยัง port TCP 8080 ของ container นี้
- db
image: postgres:10.4
ผมใช้ image เป็น postgres:10.4 ซึ่งอันนี้ทาง docker จะทำการpull
มาให้เลยมาเรารันคำสั่งdocker-compose up
container_name: db
ตั้งชื่อ container ว่า dbvolumes
คือ การบอกว่าเราจะเก็บข้อมูลไว้ที่ไหน เป็นการ map path จาก/var/db/data/postgresql
ไปยัง/var/lib/postgresql
environment
คือ กำหนดค่า config ต่างๆให้กับตัวแปรตาม environment คล้ายกับไฟล์ .envports
เปิด port ตรงกันคือ 5432
สร้าง Dockerfile
# Build stage 1 : Compile & build application
FROM maven:3.8.7-openjdk-18-slim AS build
COPY src /home/app/src
COPY pom.xml /home/app
RUN mvn -f /home/app/pom.xml clean package -DskipTests
# Build stage 2 : Run application
FROM openjdk:18-jdk
COPY /home/app/target/auth-0.0.1-SNAPSHOT.jar /home/app/build/auth.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","-Dserver.port=8080","/home/app/build/auth.jar"]
FROM maven:3.8.7-openjdk-18-slim AS build
เป็นการบอกว่าเราจะใช้ Docker imagemaven:3.8.7-openjdk-18-slim
และตั้งชื่อ build stage ว่าbuild
เพื่อเรียกใช้ต่อไปCOPY src /home/app/src
เป็นการ Copy foldersrc
ไปยัง/home/app/src
ใน containerCOPY pom.xml /home/app
เป็นการ Copy ไฟล์pom.xml
ไปยัง/home/app
ใน containerRUN mvn -f /home/app/pom.xml clean package -DskipTests
เป็น Compile ด้วย Maven และจะได้ไฟล์.jar
ออกมาใน/home/app/target
FROM openjdk:18-jdk
เป็นการ Build stage 2 สำหรับ Run application เราจะใช้ imageopenjdk:18-jdk
ซึ่งต้องตรงตาม Java version ใน ProjectCOPY --from=build /home/app/target/auth-0.0.1-SNAPSHOT.jar /home/app/build/auth.jar
เป็นการ Copy ข้าม Stage เพราะว่าเรา Build file.jar
ไว้ใน stage 1 ที่เราตั้งชื่อว่าbuild
Copy จาก/home/app/target/auth-0.0.1-SNAPSHOT.jar
มายัง/home/app/build/auth.jar
ใน stage นี้EXPOSE 8080
เราจะ Run container นี้ด้วย port 8080ENTRYPOINT ["java","-jar","-Dserver.port=8080","/home/app/build/auth.jar"]
เป็นการ Run java project จากไฟล์.jar
ที่เรา Build และ Copy มา
docker-compose up
ใช้คำสั่ง ➜ auth-spring-jwt-docker git:(master) ✗ docker-compose up
[+] Running 15/16
⠿ db Pulled 10.4s
⠿ 74a932489409 Pull complete 2.5s
⠿ a082493027d6 Pull complete 2.6s
⠿ c73249adce99 Pull complete 2.7s
⠿ 2d6bce912451 Pull complete 2.7s
⠿ 89de2b1286c6 Pull complete 2.9s
⠿ d8c1cda7fb4f Pull complete 2.9s
⠿ 04bf7b5c55f8 Pull complete 3.4s
⠿ cad383a66059 Pull complete 3.4s
⠿ 6733f43ea70c Pull complete 6.9s
⠿ 707e351a3a19 Pull complete 6.9s
⠿ 86646d71e3bf Pull complete 6.9s
⠿ 0847117bacb4 Pull complete 7.0s
⠿ af6840a317bd Pull complete 7.0s
⠿ 7679a4a015cb Pull complete 7.1s
⠿ app Warning 4.1s
[+] Building 4.1s (14/14) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 32B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/openjdk:18-jdk 3.9s
=> [internal] load metadata for docker.io/library/maven:3.8.7-openjdk-18-slim 2.9s
=> [auth] library/openjdk:pull token for registry-1.docker.io 0.0s
=> [auth] library/maven:pull token for registry-1.docker.io 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 3.71kB 0.0s
=> [build 1/4] FROM docker.io/library/maven:3.8.7-openjdk-18-slim@sha256:0ccb24680338459567324858307f64ccff55e4e5904487b9b376dcf672f6dafa 0.0s
=> [stage-1 1/2] FROM docker.io/library/openjdk:18-jdk@sha256:9b448de897d211c9e0ec635a485650aed6e28d4eca1efbc34940560a480b3f1f 0.0s
=> CACHED [build 2/4] COPY src /home/app/src 0.0s
=> CACHED [build 3/4] COPY pom.xml /home/app 0.0s
=> CACHED [build 4/4] RUN mvn -f /home/app/pom.xml clean package -DskipTests 0.0s
=> CACHED [stage-1 2/2] COPY --from=build /home/app/target/auth-0.0.1-SNAPSHOT.jar /home/app/build/auth.jar 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:19782626f748b415a9e51dd30af8c2fcb720f814d4192df893cae17a8258dd89 0.0s
=> => naming to docker.io/library/api_backend:latest 0.0s
[+] Running 3/2
⠿ Network auth-spring-jwt-docker_default Created 0.0s
⠿ Container db Created 0.3s
⠿ Container app Created 0.1s
Attaching to app, db
db | The files belonging to this database system will be owned by user "postgres".
db | This user must also own the server process.
db |
db | The database cluster will be initialized with locale "en_US.utf8".
db | The default database encoding has accordingly been set to "UTF8".
db | The default text search configuration will be set to "english".
db |
db | Data page checksums are disabled.
db |
db | fixing permissions on existing directory /var/lib/postgresql/data ... ok
db | creating subdirectories ... ok
db | selecting default max_connections ... 100
db | selecting default shared_buffers ... 128MB
db | selecting dynamic shared memory implementation ... posix
db | creating configuration files ... ok
db | running bootstrap script ... ok
db | performing post-bootstrap initialization ... ok
app |
app | . ____ _ __ _ _
app | /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
app | ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
app | \\/ ___)| |_)| | | | | || (_| | ) ) ) )
app | ' |____| .__|_| |_|_| |_\__, | / / / /
app | =========|_|==============|___/=/_/_/_/
app | :: Spring Boot :: (v3.0.3)
app |
app | 2023-03-12T07:45:54.457Z INFO 1 --- [ main] com.demo.auth.AuthApplication : Starting AuthApplication v0.0.1-SNAPSHOT using Java 18.0.2.1 with PID 1 (/home/app/build/auth.jar started by root in /)
app | 2023-03-12T07:45:54.459Z INFO 1 --- [ main] com.demo.auth.AuthApplication : No active profile set, falling back to 1 default profile: "default"
db | syncing data to disk ... ok
db |
db | WARNING: enabling "trust" authentication for local connections
db | You can change this by editing pg_hba.conf or using the option -A, or
db | --auth-local and --auth-host, the next time you run initdb.
db |
db | Success. You can now start the database server using:
db |
db | pg_ctl -D /var/lib/postgresql/data -l logfile start
db |
db | waiting for server to start....2023-03-12 07:45:54.495 UTC [41] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
db | 2023-03-12 07:45:54.507 UTC [42] LOG: database system was shut down at 2023-03-12 07:45:54 UTC
db | 2023-03-12 07:45:54.514 UTC [41] LOG: database system is ready to accept connections
db | done
db | server started
db | ALTER ROLE
db |
db |
db | /usr/local/bin/docker-entrypoint.sh: ignoring /docker-entrypoint-initdb.d/*
db |
db | 2023-03-12 07:45:54.648 UTC [41] LOG: received fast shutdown request
db | waiting for server to shut down....2023-03-12 07:45:54.651 UTC [41] LOG: aborting any active transactions
db | 2023-03-12 07:45:54.662 UTC [41] LOG: worker process: logical replication launcher (PID 48) exited with exit code 1
db | 2023-03-12 07:45:54.662 UTC [43] LOG: shutting down
db | 2023-03-12 07:45:54.682 UTC [41] LOG: database system is shut down
db | done
db | server stopped
db |
db | PostgreSQL init process complete; ready for start up.
db |
db | 2023-03-12 07:45:54.758 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
db | 2023-03-12 07:45:54.759 UTC [1] LOG: listening on IPv6 address "::", port 5432
db | 2023-03-12 07:45:54.761 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
db | 2023-03-12 07:45:54.774 UTC [59] LOG: database system was shut down at 2023-03-12 07:45:54 UTC
db | 2023-03-12 07:45:54.779 UTC [1] LOG: database system is ready to accept connections
app | 2023-03-12T07:45:55.001Z INFO 1 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
app | 2023-03-12T07:45:55.041Z INFO 1 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 34 ms. Found 3 JPA repository interfaces.
app | 2023-03-12T07:45:55.384Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
app | 2023-03-12T07:45:55.394Z INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
app | 2023-03-12T07:45:55.394Z INFO 1 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.5]
app | 2023-03-12T07:45:55.454Z INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/api] : Initializing Spring embedded WebApplicationContext
app | 2023-03-12T07:45:55.455Z INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 962 ms
app | 2023-03-12T07:45:55.539Z INFO 1 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
app | 2023-03-12T07:45:55.644Z INFO 1 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@71c5b236
app | 2023-03-12T07:45:55.645Z INFO 1 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
db | 2023-03-12 07:45:55.648 UTC [66] ERROR: relation "member_type" does not exist at character 13
db | 2023-03-12 07:45:55.648 UTC [66] STATEMENT: INSERT INTO member_type(id, created_by, created_date, updated_by, updated_date, "version","name") VALUES(1, 'SYSTEM', current_timestamp, null, null, 0,'PLATINUM')
db | 2023-03-12 07:45:55.654 UTC [66] ERROR: relation "member_type" does not exist at character 13
db | 2023-03-12 07:45:55.654 UTC [66] STATEMENT: INSERT INTO member_type(id, created_by, created_date, updated_by, updated_date, "version","name") VALUES(2, 'SYSTEM', current_timestamp, null, null, 0,'GOLD')
db | 2023-03-12 07:45:55.654 UTC [66] ERROR: relation "member_type" does not exist at character 13
db | 2023-03-12 07:45:55.654 UTC [66] STATEMENT: INSERT INTO member_type(id, created_by, created_date, updated_by, updated_date, "version","name") VALUES(3, 'SYSTEM', current_timestamp, null, null, 0,'SILVER')
db | 2023-03-12 07:45:55.655 UTC [66] ERROR: relation "roles" does not exist at character 13
db | 2023-03-12 07:45:55.655 UTC [66] STATEMENT: INSERT INTO roles(id, created_by, created_date, updated_by, updated_date, "version","name") VALUES(1, 'SYSTEM', current_timestamp, null, null, 0,'ROLE_USER')
db | 2023-03-12 07:45:55.656 UTC [66] ERROR: relation "roles" does not exist at character 13
db | 2023-03-12 07:45:55.656 UTC [66] STATEMENT: INSERT INTO roles(id, created_by, created_date, updated_by, updated_date, "version","name") VALUES(2, 'SYSTEM', current_timestamp, null, null, 0,'ROLE_MODERATOR')
db | 2023-03-12 07:45:55.656 UTC [66] ERROR: relation "roles" does not exist at character 13
db | 2023-03-12 07:45:55.656 UTC [66] STATEMENT: INSERT INTO roles(id, created_by, created_date, updated_by, updated_date, "version","name") VALUES(3, 'SYSTEM', current_timestamp, null, null, 0,'ROLE_ADMIN')
app | 2023-03-12T07:45:55.704Z INFO 1 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
app | 2023-03-12T07:45:55.736Z INFO 1 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.1.7.Final
app | 2023-03-12T07:45:55.916Z INFO 1 --- [ main] SQL dialect : HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
app | 2023-03-12T07:45:56.373Z WARN 1 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Warning Code: 0, SQLState: 00000
app | 2023-03-12T07:45:56.373Z WARN 1 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : constraint "uk_9q63snka3mdh91as4io72espi" of relation "users" does not exist, skipping
app | 2023-03-12T07:45:56.376Z WARN 1 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Warning Code: 0, SQLState: 00000
app | 2023-03-12T07:45:56.376Z WARN 1 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : constraint "uk_ifjsuxbpqa2ug9srnnhskrd9j" of relation "users" does not exist, skipping
app | 2023-03-12T07:45:56.379Z WARN 1 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Warning Code: 0, SQLState: 00000
app | 2023-03-12T07:45:56.379Z WARN 1 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : constraint "uk_r43af9ap4edm43mmtq01oddj6" of relation "users" does not exist, skipping
app | 2023-03-12T07:45:56.383Z INFO 1 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
app | 2023-03-12T07:45:56.389Z INFO 1 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
app | 2023-03-12T07:45:56.743Z WARN 1 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
app | 2023-03-12T07:45:57.371Z INFO 1 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@659feb22, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@3468ee6e, org.springframework.security.web.context.SecurityContextHolderFilter@4833eff3, org.springframework.security.web.header.HeaderWriterFilter@70c69586, org.springframework.web.filter.CorsFilter@2f4b98f6, org.springframework.security.web.csrf.CsrfFilter@717d7587, org.springframework.security.web.authentication.logout.LogoutFilter@e38f0b7, com.demo.auth.security.jwt.AuthTokenFilter@51d387d3, com.demo.auth.security.csrf.CsrfCookieFilter@590c73d3, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@56928e17, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@dd2856e, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@421def93, org.springframework.security.web.session.SessionManagementFilter@3976ebfa, org.springframework.security.web.access.ExceptionTranslationFilter@6a0f2853, org.springframework.security.web.access.intercept.AuthorizationFilter@345cbf40]
app | 2023-03-12T07:45:57.556Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '/api'
app | 2023-03-12T07:45:57.563Z INFO 1 --- [ main] com.demo.auth.AuthApplication : Started AuthApplication in 3.386 seconds (process running for 3.765)
อันนี้คือ Run สำเร็จทั้ง
app
และdb
ถ้าเรา Run ครั้งแรกอาจจะช้าหน่อย เพราะ Docker ต้องไป Pull ของต่างๆลงมาเพื่อสร้าง Images, Containers
ดูผ่าน Docker Application
- ลองเข้า
localhost:8008
ถ้าเป็น Project นี้ต้องเข้า http://localhost:8008/api/swagger-ui/index.html - ลองต่อ Database ผ่าน Database client ดู Config ต่างๆ ก็ตามที่กำหนดไว้ใน
docker-compose.yml
จะเห็นว่าขั้นตอนการ Config น้อยมากๆ เพียงแค่ Setup 2 file กับอีก 1 คำสั่ง ก็สามารถ Run Spring Boot ได้แล้ว ไม่ว่า Computer หรือ Server ของคุณจะเป็น OS อะไรก็ตาม ขอเพียงแค่มี Docker, Docker Compose ไม่ต้องมาเสียเวลา Setup environment อีกต่างหาก ถ้าเราไม่ใช้ Docker ในเครื่องเราจะต้องมี Maven , Java , PostgresSQL และเป็น Version ตามที่ Project กำหนดด้วยเพื่อที่จะ Run Spring Boot