πŸ’‘ κ°œμš”

μ»¨ν…Œμ΄λ„ˆ ν™˜κ²½μ—μ„œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μš΄μ˜ν•  λ•Œ, μ •ν™•ν•œ νƒ€μž„μ‘΄(Timezone) 섀정은 맀우 μ€‘μš”ν•˜λ‹€. 둜그 기둝, λ°μ΄ν„°λ² μ΄μŠ€ μ‹œκ°„ 동기화, μŠ€μΌ€μ€„λ§λœ μž‘μ—… λ“± λ‹€μ–‘ν•œ μ˜μ—­μ—μ„œ μ‹œκ°„ 정보가 μΌκ΄€λ˜μ§€ μ•ŠμœΌλ©΄ μ˜ˆμƒμΉ˜ λͺ»ν•œ λ¬Έμ œλ‚˜ 였λ₯˜κ°€ λ°œμƒν•  수 μžˆλ‹€. 이 λ¬Έμ„œμ—μ„œλŠ” Docker μ»¨ν…Œμ΄λ„ˆμ˜ νƒ€μž„μ‘΄μ„ μ„€μ •ν•˜λŠ” λ‹€μ–‘ν•œ 방법과 각 λ°©λ²•μ˜ νŠΉμ§•μ„ μ‚΄νŽ΄λ³΄μ•˜λ‹€.

πŸ’¬ 이슈

μΏ λ²„λ„€ν‹°μŠ€(Kubernetes) 상에 λ™μž‘ 쀑인 νŒŒλ“œ(Pod)λ§ˆλ‹€ μ„œλ‘œ λ‹€λ₯Έ timezone을 κ°€μ§€κ³  μžˆμ—ˆκ³ , 이λ₯Ό GMT+9 (Asia/Seoul)둜 일괄 λ³€ν™˜ν•˜λŠ” 방법이 ν•„μš”ν–ˆλ‹€.

πŸ§— ν•΄κ²°

μ»¨ν…Œμ΄λ„ˆμ˜ νƒ€μž„μ‘΄μ„ μ„€μ •ν•˜λŠ” μ£Όμš” 방법은 λ‹€μŒκ³Ό κ°™λ‹€.

1. Dockerfile을 ν†΅ν•œ μ„€μ • (Build-time)

μ»¨ν…Œμ΄λ„ˆ 이미지λ₯Ό λΉŒλ“œν•˜λŠ” μ‹œμ μ— Dockerfile λ‚΄μ—μ„œ νƒ€μž„μ‘΄μ„ μ„€μ •ν•˜λŠ” 방법이닀. 이 방법은 μ»¨ν…Œμ΄λ„ˆκ°€ μ‹œμž‘λ  λ•Œλ§ˆλ‹€ λ³„λ„μ˜ μ„€μ • 없이 μΌκ΄€λœ νƒ€μž„μ‘΄μ„ 보μž₯ν•˜μ—¬ κ°€μž₯ ꢌμž₯λ˜λŠ” 방식 쀑 ν•˜λ‚˜μ΄λ‹€.

  • 원리: λŒ€λΆ€λΆ„μ˜ Linux μ‹œμŠ€ν…œμ€ /etc/timezone νŒŒμΌμ— νƒ€μž„μ‘΄ 정보λ₯Ό μ €μž₯ν•˜κ±°λ‚˜, /etc/localtime 파일이 μ‹€μ œ νƒ€μž„μ‘΄ 데이터 파일(예: /usr/share/zoneinfo/Asia/Seoul)을 κ°€λ¦¬ν‚€λŠ” 심볼릭 링크둜 κ΅¬μ„±λœλ‹€. tzdata νŒ¨ν‚€μ§€λŠ” μ΄λŸ¬ν•œ νƒ€μž„μ‘΄ 데이터 νŒŒμΌμ„ μ œκ³΅ν•œλ‹€. ENV TZ λͺ…λ Ήμ–΄λŠ” μ»¨ν…Œμ΄λ„ˆ λ‚΄λΆ€μ˜ ν™˜κ²½ λ³€μˆ˜λ₯Ό μ„€μ •ν•˜μ—¬ μ‹œμŠ€ν…œμ΄ ν•΄λ‹Ή νƒ€μž„μ‘΄μ„ μ‚¬μš©ν•˜λ„λ‘ μ§€μ‹œν•œλ‹€.
# μ˜ˆμ‹œ: Ubuntu/Debian 기반 이미지
FROM ubuntu:latest
 
# tzdata νŒ¨ν‚€μ§€ μ„€μΉ˜ (νƒ€μž„μ‘΄ 데이터 제곡)
# noninteractive λͺ¨λ“œλ‘œ μ„€μΉ˜ν•˜μ—¬ μ‚¬μš©μž μž…λ ₯ 없이 μ§„ν–‰
RUN apt-get update && apt-get install -y tzdata && rm -rf /var/lib/apt/lists/*
 
# ν™˜κ²½ λ³€μˆ˜ TZλ₯Ό Asia/Seoul둜 μ„€μ •
ENV TZ=Asia/Seoul
 
# μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰ λͺ…λ Ήμ–΄ (μ˜ˆμ‹œ)
CMD ["/bin/bash"]
  • μž₯점:
    • 이미지 μžμ²΄μ— νƒ€μž„μ‘΄ 정보가 ν¬ν•¨λ˜μ–΄ μžˆμ–΄ 이식성이 λ†’λ‹€.
    • μ»¨ν…Œμ΄λ„ˆ μ‹€ν–‰ μ‹œ λ³„λ„μ˜ 섀정이 ν•„μš” μ—†μ–΄ 관리가 μš©μ΄ν•˜λ‹€.
    • λΉŒλ“œ μ‹œμ μ— νƒ€μž„μ‘΄μ΄ κ³ μ •λ˜λ―€λ‘œ 예츑 κ°€λŠ₯ν•˜λ‹€.
  • 단점:
    • νƒ€μž„μ‘΄ λ³€κ²½ μ‹œ 이미지λ₯Ό λ‹€μ‹œ λΉŒλ“œν•΄μ•Ό ν•œλ‹€.
    • tzdata νŒ¨ν‚€μ§€ μ„€μΉ˜λ‘œ 인해 이미지 크기가 μ•½κ°„ 증가할 수 μžˆλ‹€.

2. Volume Mountλ₯Ό ν†΅ν•œ μ„€μ • (Run-time)

호슀트 μ‹œμŠ€ν…œμ˜ νƒ€μž„μ‘΄ 정보λ₯Ό μ»¨ν…Œμ΄λ„ˆ λ‚΄λΆ€λ‘œ λ§ˆμš΄νŠΈν•˜μ—¬ μ‚¬μš©ν•˜λŠ” 방법이닀. 이 방법은 μ»¨ν…Œμ΄λ„ˆ 이미지λ₯Ό μˆ˜μ •ν•˜μ§€ μ•Šκ³  λŸ°νƒ€μž„μ— νƒ€μž„μ‘΄μ„ μœ μ—°ν•˜κ²Œ μ„€μ •ν•  수 μžˆλ‹€.

  • 원리: 호슀트 μ‹œμŠ€ν…œμ˜ /etc/localtime 파일 λ˜λŠ” /usr/share/zoneinfo 디렉토리λ₯Ό μ»¨ν…Œμ΄λ„ˆ λ‚΄λΆ€μ˜ /etc/localtime 경둜둜 λ§ˆμš΄νŠΈν•œλ‹€. μ»¨ν…Œμ΄λ„ˆλŠ” 마운트된 호슀트의 νƒ€μž„μ‘΄ 정보λ₯Ό μ‚¬μš©ν•˜κ²Œ λœλ‹€.

Docker CLI

$ docker run -d \
  -v /etc/localtime:/etc/localtime:ro \
  -v /usr/share/zoneinfo/Asia/Seoul:/etc/localtime:ro # νŠΉμ • νƒ€μž„μ‘΄ 파일 마운트 (더 λͺ…μ‹œμ )
  your-image-name
  • /etc/localtime:/etc/localtime:ro: 호슀트의 ν˜„μž¬ νƒ€μž„μ‘΄ 섀정을 μ»¨ν…Œμ΄λ„ˆλ‘œ 읽기 μ „μš©(read-only)으둜 λ§ˆμš΄νŠΈν•œλ‹€.
  • /usr/share/zoneinfo/Asia/Seoul:/etc/localtime:ro: 호슀트의 νŠΉμ • νƒ€μž„μ‘΄ 데이터 파일(Asia/Seoul)을 μ»¨ν…Œμ΄λ„ˆμ˜ /etc/localtime둜 λ§ˆμš΄νŠΈν•œλ‹€. 이 방법은 호슀트의 ν˜„μž¬ νƒ€μž„μ‘΄ μ„€μ •κ³Ό λ¬΄κ΄€ν•˜κ²Œ μ»¨ν…Œμ΄λ„ˆμ— νŠΉμ • νƒ€μž„μ‘΄μ„ κ°•μ œν•  λ•Œ μœ μš©ν•˜λ‹€.

Kubernetes ν™˜κ²½

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      containers:
      - name: my-container
        image: your-image-name
        volumeMounts:
        - name: tz-config
          mountPath: /etc/localtime # /etc/localtime νŒŒμΌμ„ 마운트
          readOnly: true
      volumes:
      - name: tz-config
        hostPath:
          path: /usr/share/zoneinfo/Asia/Seoul # 호슀트의 Asia/Seoul νƒ€μž„μ‘΄ νŒŒμΌμ„ μ‚¬μš©
          type: File # νŒŒμΌμž„μ„ λͺ…μ‹œ
  • μž₯점:
    • 이미지 μž¬λΉŒλ“œ 없이 λŸ°νƒ€μž„μ— νƒ€μž„μ‘΄μ„ λ³€κ²½ν•  수 μžˆλ‹€.
    • 호슀트의 νƒ€μž„μ‘΄κ³Ό λ™κΈ°ν™”ν•˜κ±°λ‚˜, ν˜ΈμŠ€νŠΈμ— μ‘΄μž¬ν•˜λŠ” νŠΉμ • νƒ€μž„μ‘΄ νŒŒμΌμ„ μ„ νƒν•˜μ—¬ μ‚¬μš©ν•  수 μžˆλ‹€.
  • 단점:
    • μ»¨ν…Œμ΄λ„ˆκ°€ 호슀트 μ‹œμŠ€ν…œμ— μ˜μ‘΄ν•˜κ²Œ λœλ‹€. 호슀트의 νƒ€μž„μ‘΄ 섀정이 λ³€κ²½λ˜λ©΄ μ»¨ν…Œμ΄λ„ˆμ—λ„ 영ν–₯을 λ―ΈμΉœλ‹€.
    • hostPath μ‚¬μš© μ‹œ λ…Έλ“œ κ°„ 이식성이 μ €ν•˜λ  수 μžˆλ‹€ (λͺ¨λ“  λ…Έλ“œμ— λ™μΌν•œ νƒ€μž„μ‘΄ 파일이 μ‘΄μž¬ν•΄μ•Ό ν•œλ‹€).
    • λ³΄μ•ˆμƒμ˜ 이유둜 hostPath μ‚¬μš©μ„ μ œν•œν•˜λŠ” ν™˜κ²½λ„ μžˆλ‹€.

3. ν™˜κ²½ λ³€μˆ˜ TZ만 μ„€μ • (Run-time)

μ»¨ν…Œμ΄λ„ˆ 이미지에 이미 tzdata νŒ¨ν‚€μ§€κ°€ μ„€μΉ˜λ˜μ–΄ μžˆλŠ” 경우, TZ ν™˜κ²½ λ³€μˆ˜λ§Œ μ„€μ •ν•˜μ—¬ νƒ€μž„μ‘΄μ„ λ³€κ²½ν•  수 μžˆλ‹€.

  • 원리: TZ ν™˜κ²½ λ³€μˆ˜λŠ” C 라이브러리(glibc)μ—μ„œ μ‹œκ°„μ„ μ²˜λ¦¬ν•  λ•Œ μ°Έμ‘°ν•˜λŠ” νƒ€μž„μ‘΄μ„ μ§€μ •ν•œλ‹€. tzdata νŒ¨ν‚€μ§€κ°€ μ„€μΉ˜λ˜μ–΄ μžˆλ‹€λ©΄, /usr/share/zoneinfo κ²½λ‘œμ— λ‹€μ–‘ν•œ νƒ€μž„μ‘΄ 데이터 파일이 μ‘΄μž¬ν•˜λ©°, TZ λ³€μˆ˜λŠ” 이 쀑 μ–΄λ–€ νŒŒμΌμ„ μ‚¬μš©ν• μ§€ μ•Œλ €μ£ΌλŠ” 역할을 ν•œλ‹€.

Docker CLI

$ docker run -d \
  -e TZ=Asia/Seoul \
  your-image-name

Kubernetes ν™˜κ²½

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      containers:
      - name: my-container
        image: your-image-name
        env:
        - name: TZ
          value: Asia/Seoul
  • μž₯점:
    • κ°€μž₯ κ°„λ‹¨ν•˜κ³  μœ μ—°ν•œ 방법이닀.
    • 이미지 μž¬λΉŒλ“œ 없이 λŸ°νƒ€μž„μ— νƒ€μž„μ‘΄μ„ λ³€κ²½ν•  수 μžˆλ‹€.
  • 단점:
    • μ»¨ν…Œμ΄λ„ˆ 이미지 내뢀에 tzdata νŒ¨ν‚€μ§€κ°€ 미리 μ„€μΉ˜λ˜μ–΄ μžˆμ–΄μ•Ό ν•œλ‹€. κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ TZ ν™˜κ²½ λ³€μˆ˜κ°€ μ œλŒ€λ‘œ μž‘λ™ν•˜μ§€ μ•Šκ±°λ‚˜ κΈ°λ³Έ UTC둜 λ™μž‘ν•  수 μžˆλ‹€.

4. timedatectl λͺ…λ Ήμ–΄ μ‚¬μš© (μ»¨ν…Œμ΄λ„ˆ λ‚΄λΆ€)

일뢀 μ»¨ν…Œμ΄λ„ˆ μ΄λ―Έμ§€μ—λŠ” systemdκ°€ ν¬ν•¨λ˜μ–΄ μžˆμ§€ μ•Šμ•„ timedatectl λͺ…λ Ήμ–΄λ₯Ό 직접 μ‚¬μš©ν•  수 μ—†λŠ” κ²½μš°κ°€ λ§Žλ‹€. ν•˜μ§€λ§Œ 디버깅 λͺ©μ μ΄λ‚˜ νŠΉμ • 베이슀 μ΄λ―Έμ§€μ—μ„œλŠ” μœ μš©ν•  수 μžˆλ‹€.

# μ»¨ν…Œμ΄λ„ˆ λ‚΄λΆ€μ—μ„œ μ‹€ν–‰ (μ˜ˆμ‹œ)
$ docker exec -it <container_id> /bin/bash
# μ»¨ν…Œμ΄λ„ˆ μ‰˜ λ‚΄λΆ€μ—μ„œ
$ timedatectl set-timezone Asia/Seoul
  • μž₯점: μ»¨ν…Œμ΄λ„ˆ λ‚΄λΆ€μ—μ„œ 직접 μ‹œμŠ€ν…œ νƒ€μž„μ‘΄μ„ μ„€μ •ν•˜λŠ” 것과 μœ μ‚¬ν•œ 효과λ₯Ό λ‚Ό 수 μžˆλ‹€.
  • 단점: λŒ€λΆ€λΆ„μ˜ κ²½λŸ‰ μ»¨ν…Œμ΄λ„ˆ μ΄λ―Έμ§€μ—λŠ” systemdλ‚˜ timedatectl이 ν¬ν•¨λ˜μ–΄ μžˆμ§€ μ•Šμ•„ μ‚¬μš©ν•˜κΈ° μ–΄λ ΅λ‹€. μ»¨ν…Œμ΄λ„ˆ μž¬μ‹œμž‘ μ‹œ 섀정이 μœ μ§€λ˜μ§€ μ•Šμ„ 수 μžˆλ‹€.

βœ… 확인

μ»¨ν…Œμ΄λ„ˆ λ‚΄λΆ€μ—μ„œ νƒ€μž„μ‘΄μ΄ μ˜¬λ°”λ₯΄κ²Œ μ„€μ •λ˜μ—ˆλŠ”μ§€ ν™•μΈν•˜λŠ” 방법이닀.

# date λͺ…λ Ήμ–΄ μ‚¬μš©
$ date
Tue Nov 22 07:44:20 KST 2022 # μ˜ˆμ‹œ 좜λ ₯: KST (ν•œκ΅­ ν‘œμ€€μ‹œ)둜 ν‘œμ‹œλ˜μ—ˆλ‹€.
 
# timedatectl λͺ…λ Ήμ–΄ μ‚¬μš© (timedatectl이 μ„€μΉ˜λœ 경우)
$ timedatectl
               Local time: ν™” 2022-11-22 07:44:20 KST
           Universal time: μ›” 2022-11-21 22:44:20 UTC
                 RTC time: μ›” 2022-11-21 22:44:20
                Time zone: Asia/Seoul (KST, +0900)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

πŸš€ μš”μ•½

방법μž₯μ λ‹¨μ κΆŒμž₯ μ‹œλ‚˜λ¦¬μ˜€
Dockerfile이미지에 νƒ€μž„μ‘΄ λ‚΄μž₯, 이식성 λ†’μŒ, μΌκ΄€λœ μ„€μ • 보μž₯이미지 μž¬λΉŒλ“œ ν•„μš”, 이미지 크기 μ•½κ°„ μ¦κ°€λŒ€λΆ€λΆ„μ˜ ν”„λ‘œλ•μ…˜ ν™˜κ²½, μΌκ΄€λœ νƒ€μž„μ‘΄μ΄ ν•„μš”ν•œ 경우
Volume Mount이미지 μž¬λΉŒλ“œ λΆˆν•„μš”, λŸ°νƒ€μž„ μœ μ—°μ„±, ν˜ΈμŠ€νŠΈμ™€ 동기화 κ°€λŠ₯호슀트 μ˜μ‘΄μ„±, λ…Έλ“œ κ°„ 이식성 μ €ν•˜, hostPath λ³΄μ•ˆ λ¬Έμ œλŸ°νƒ€μž„μ— νƒ€μž„μ‘΄ 변경이 μž¦κ±°λ‚˜ ν˜ΈμŠ€νŠΈμ™€ 동기화가 ν•„μš”ν•œ 경우
ν™˜κ²½ λ³€μˆ˜ TZκ°€μž₯ 간단, 이미지 μž¬λΉŒλ“œ λΆˆν•„μš”, λŸ°νƒ€μž„ μœ μ—°μ„±tzdata μ„€μΉ˜ ν•„μˆ˜, μ„€μΉ˜λ˜μ§€ μ•Šμ€ 경우 κΈ°λ³Έ UTCtzdataκ°€ ν¬ν•¨λœ κ²½λŸ‰ 이미지 μ‚¬μš© μ‹œ, κ°„λ‹¨ν•œ μ„€μ •
timedatectlμ»¨ν…Œμ΄λ„ˆ λ‚΄λΆ€μ—μ„œ 직접 μ„€μ • κ°€λŠ₯λŒ€λΆ€λΆ„μ˜ κ²½λŸ‰ μ΄λ―Έμ§€μ—μ„œ μ‚¬μš© λΆˆκ°€, μ»¨ν…Œμ΄λ„ˆ μž¬μ‹œμž‘ μ‹œ μ„€μ • μœ μ§€ μ•ˆλ¨, 디버깅 λͺ©μ  μ™Έ λΉ„κΆŒμž₯디버깅 λͺ©μ , νŠΉμ • 베이슀 이미지 μ‚¬μš© μ‹œ

πŸ”— μ°Έκ³