iT邦幫忙

2026 iThome 鐵人賽

DAY 8
0

如果要說明使用Podman Quadlet的最大好處作為說服理由,我一定選.container裡的Environment變數。它不僅可以替代運行環境時由Shell載入profile檔的環境變數,連JVM的環境變數以及Spring Boot的application.yaml或application.properties的環境變數也都取代。

這好處在哪?比如application.yaml在開發環境的DB連線資訊,可以不用為了SIT或UAT環境,指定profile重包一版image到SIT或UAT環境,只需在.container設定Environment變數替代application.yaml的DB連線資訊,image根本不用指定profile來重包,這是其一。
其二是若有些環境變數是在SIT與UAT才會用到,但開發環境不會用到,Environment變數只需要在SIT/UAT環境設定就行。所以這篇主旨是講.container的Environment怎麼架空profile。


在a2a專案,應客戶要求,DB、FTP的密碼要存在RedHat主機的/a2a/AP下的某密碼檔,該密碼檔與解密用的jar檔是客戶提供的,公司的開發環境是可以比照建置/a2a/AP子目錄及密碼檔,但本機開發的工程師大多是Windows及Mac,寫好程式不能在本機直接測終究是個麻煩,Podman Quadlet的.container的Environment的設計根本可以讓工程師忘了密碼檔的存在。
第一個要解決的問題是rootless帳號存取根目錄開始的子目錄讀取權限,也就是/a2a,所以在第六天的文章有講解法就是為了可以讓spring boot順利讀取。
在spring boot的application.yaml原本DB連線設定如下:

spring:
  datasource:
    driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
    url: jdbc:sqlserver://10.1.2.5:1433;DatabaseName=A2A;SelectMethod=cursor;sendStringParametersAsUnicode=true;encrypt=false;trustServerCertificate=true
    username: scott
    password: tigher
    hikari:
      maxinum-pool-size: 10
      connection-timeout: 0

若想直接改密碼值,直接在.container設定下的[Container]標籤加Environment就可以覆蓋:

[Container]
Environment=spring.datasource.password=newpassword

當然連DB的URL、帳號等參數也能這樣狸貓換太子、李代桃僵、抓交替、奪舍...。


前面說到客戶要求是用他們提供的jar去讀/a2a/AP下的密碼檔,所以等於Spring Boot啟動後,在載入application.yaml參數就要做解出密碼的動作,所以新增一支程式如下:

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import customer.pub.UserInfo;  // 客戶提供的解密元件

import java.util.HashMap;
import java.util.Map;

@Slf4j
public class A2AEnvironmentPostProcessor implements EnvironmentPostProcessor {

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        boolean enabled = environment.getProperty(
                "a2a.dynamic-password.enabled",
                Boolean.class,
                false
        );

        if (!enabled) {
            return;
        }

        String datasourceUsername = environment.getProperty("spring.datasource.username");
        String hostname = environment.getProperty("a2a.db.hostname");

        if (datasourceUsername == null || datasourceUsername.trim().isEmpty()) {
            throw new IllegalStateException("spring.datasource.username 未設定");
        }

        if (hostname == null || hostname.trim().isEmpty()) {
            throw new IllegalStateException("a2a.db.hostname 未設定");
        }

        try {
            String password = new UserInfo().GetUserValue(hostname, datasourceUsername);
            if (password == null || password.trim().isEmpty()) {
                throw new IllegalStateException("由 UserInfo 取得的 DB 密碼為空值");
            }

            Map<String, Object> map = new HashMap<>();
            map.put("spring.datasource.password", password);

            environment.getPropertySources().addFirst(new MapPropertySource("customerDbPassword", map));

        } catch (Exception e) {
            throw new IllegalStateException("透過 UserInfo 取得 DB 密碼失敗", e);
        }
    }
}
  • 繼承EnvironmentPostProcessor的類別在執行postProcessEnvironment這個method一開始就檢查環境變數a2a.dynamic-password.enabled是否為true,若不存在或不是true就直接結束,所以個人開發環境不會有a2a.dynamic-password.enabled這環境變數,就沒有讀密碼檔的問題。
  • 客戶提供的UserInfo類別需要帳號及hostname為參數,它自己會去/a2a/AP下找密碼檔解密,所以postProcessEnvironment的ConfigurableEnvironment environment參數就已載包含application.yaml等所有環境變數,自然而然就容易取得帳號(spring.datasource.username)及hostname(a2a.db.hostname)。
    而在.container設定如下:
[Container]
Environment=a2a.db.hostname=10.1.2.5
Environment=a2a.dynamic-password.enabled=true

上一篇
容器上的芳鄰:.Volume
下一篇
Quadlet檔的ABC
系列文
Podman Quadlet-容器服務化(在單體系統到微服務之間)10
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言