เมื่อการพัฒนาระบบ ได้มาถึงจุดที่ต้องทำการแยก environments ที่แตกต่างกัน จากตัวอย่างที่เห็นได้ชัด ๆ เช่น endpoint ของ API ที่ dev ก็จะใช้เป็น endpoint เพื่อสำหรับ dev เท่านั้น แต่เมื่ออยากที่จะ deploy เพื่อใช้งานบน production ก็จะมี endpoint ของ API เวอร์ชั่นที่เป็น ของ production จริงและความต้องการแบบนี้ สำหรับ project ที่ build ด้วย spring boot จะแก้ปัญหาได้อย่างไร มาทำความรู้จัก spring profiles กัน เพื่อช่วยให้ระบบสามารถแยก constant variables ตาม environment ต้องทำอย่างไร
ทดลองสร้าง Simple Project เพื่อพิสูจน์ความต้องการในครั้งนี้
โดยปกติ spring boot เมื่อสร้าง project ด้วย spring initialize เช็คโครงสร้าง project จะสังเกตุเห็นไฟล์ src/main/resources/application.properties ถ้าเราต้องการที่จะแยก environment dev , staging ,prod ก็ต้องจัดการไฟล์นี้ใหม่
- เพิ่มไฟล์ตาม environment ที่ต้องการ ตัวอย่างข้างล่างนี้จะทำตัวอย่าง แยก dev ,prod เพื่อใช้เป็นตัวอย่างง่าย ๆ โดยจะทำการ copy file application.properties มาตั้งชื่อใหม่ให้เป็นตามแต่ละ environment แบบนี้
- dev: src/main/resources/application-dev.properties
- prod: src/main/resources/application-prod.properties
- เพิ่มเติมเนื้อหาข้างในไฟล์ application-dev.properties และ application-prod.properties
#file application-dev.properties
server.port=8090
api.endpoint="dev.poolsawat.com"
#file application-prod.properties
server.port=9090
api.endpoint="prod.poolsawat.com"
โดยกำหนดให้
server.port ของ dev start ที่ port 8090 , prod start ที่ port 9090
api.endpoint ของ dev ใช้ endpoint ที่ “dev.poolsawat.com” , prod จะใช้ที่ “prod.poolsawat.com”
- แก้ไขไฟล์ Application.java (ไฟล์ สำหรับใช้ boot SpringApplication.run(PoolsawatApplication.class, args); )
package com.poolsawat.starter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class PoolsawatApplication implements CommandLineRunner{
private static final Logger logger = LoggerFactory.getLogger(PoolsawatApplication.class);
@Value("${api.endpoint}")
private String apiEndpointUrl;
@Value("${server.port}")
private String serverPort;
public static void main(String[] args) {
SpringApplication.run(PoolsawatApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
logger.info("server.port ::=="+serverPort);
logger.info("api.endpoint ::=="+apiEndpointUrl);
logger.info("spring boot loaded");
}
}
เริ่มสั่ง start server ที่ env dev (ส่ง argrument -Dspring-boot.run.profiles=dev)
$ mvn -s D:/pool13433/programs/.m2/settings.xml spring-boot:run -Dspring-boot.run.profiles=dev
...
2021-03-08 13:29:35.407 INFO 12204 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2021-03-08 13:29:35.504 INFO 12204 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8090 (http) with context path ''
2021-03-08 13:29:35.512 INFO 12204 --- [ main] c.p.starter.PoolsawatApplication : Started PoolsawatApplication in 1.253 seconds (JVM running for 1.518)
2021-03-08 13:29:35.514 INFO 12204 --- [ main] c.p.starter.PoolsawatApplication : server.port ::==8090
2021-03-08 13:29:35.514 INFO 12204 --- [ main] c.p.starter.PoolsawatApplication : api.endpoint ::=="dev.poolsawat.com"
2021-03-08 13:29:35.514 INFO 12204 --- [ main] c.p.starter.PoolsawatApplication : spring boot loaded
เริ่มสั่ง start server ที่ env prod (ส่ง argrument -Dspring.profiles.active=prod)
- pack jar ด้วย mvn install
- exc jar
$ mvn -s D:/pool13433/programs/.m2/settings.xml install -Dmaven.test.skip=true
...
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ poolsawat ---
[INFO] Installing D:\pool13433\aging\workspace\Poolsawat.Com\target\poolsawat.jar to D:\pool13433\programs\.m2\repository\com\poolsawat\poolsawat\0.0.1-SNAPSHOT\poolsawat-0.0.1-SNAPSHOT.jar
[INFO] Installing D:\pool13433\aging\workspace\Poolsawat.Com\pom.xml to D:\pool13433\programs\.m2\repository\com\poolsawat\poolsawat\0.0.1-SNAPSHOT\poolsawat-0.0.1-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
$ java -jar -Dspring.profiles.active=prod ./target/poolsawat.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.4.3)
2021-03-08 13:33:49.548 INFO 7448 --- [ main] c.p.starter.PoolsawatApplication : Starting PoolsawatApplication v0.0.1-SNAPSHOT using Java 1.8.0_281 on LAPTOP-69PUN6C7 with PID 7448 (D:\pool13433\aging\workspace\Poolsawat.Com\target\poolsawat.jar started by pool13433 in D:\pool13433\aging\workspace\Poolsawat.Com)
2021-03-08 13:33:49.550 INFO 7448 --- [ main] c.p.starter.PoolsawatApplication : The following profiles are active: prod
2021-03-08 13:33:50.698 INFO 7448 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 9090 (http)
2021-03-08 13:33:50.716 INFO 7448 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-03-08 13:33:50.717 INFO 7448 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.43]
2021-03-08 13:33:50.807 INFO 7448 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-03-08 13:33:50.807 INFO 7448 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1191 ms
2021-03-08 13:33:50.993 INFO 7448 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2021-03-08 13:33:51.156 INFO 7448 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 9090 (http) with context path ''
2021-03-08 13:33:51.167 INFO 7448 --- [ main] c.p.starter.PoolsawatApplication : Started PoolsawatApplication in 2.062 seconds (JVM running for 2.484)
2021-03-08 13:33:51.168 INFO 7448 --- [ main] c.p.starter.PoolsawatApplication : server.port ::==9090
2021-03-08 13:33:51.169 INFO 7448 --- [ main] c.p.starter.PoolsawatApplication : api.endpoint ::=="prod.poolsawat.com"
2021-03-08 13:33:51.169 INFO 7448 --- [ main] c.p.starter.PoolsawatApplication : spring boot loaded
สรุปท้ายบทความ
วิธีการที่นำเสนอนี้เป็นเพียงวิธีการแยก environment แบบนึง สามารถทำด้วยวิธีแบบอื่น ๆ ได้ แต่ผมมองว่ามันจะเป็นวิธีการที่เข้าใจง่ายที่สุด หากผู้อ่านท่านใดลองนำไปทำตามแล้วเกิดติดปัญหา สามารถฝากคำถามไว้ครับ ขอบคุณสำหรับการติดตามบทความครับ