Summary

Shlinkk3s 환경으로 설치하는 과정을 기록했습니다.

yaml

ConfigMap

컨테이너에 들어가는 환경 변수를 따로 ConfigMap의 형태로 만들어서 사용합니다.

shlink.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: shlink-config
  namespace: apps
data:
  TZ: "Asia/Seoul"
  MULTI_SEGMENT_SLUGS_ENABLED: "true"
  DEFAULT_DOMAIN: "link.junbeom.work"     # 기본 도메인 설정
  IS_HTTPS_ENABLED: "true"
  DB_DRIVER: "postgres"
  DB_HOST: "postgres-svc.core-infra.svc.cluster.local"  # PostgreSQL 서비스 주소
  REDIS_SERVERS: "tcp://shlink-redis-svc:6379"
  SHELL_VERBOSITY: "3"
 

Tip

PostgreSQL이 같은 NameSpace에 있으면 DB_HOST: postgres-svc로 수정하면 됩니다.

Redis

Shlink는 Redis가 필요한 서비스이기 때문에 Redis가 설치가 포함됩니다.

shlink.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: shlink-redis-pvc
  namespace: apps
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: local-path
  resources:
    requests:
      storage: 2Gi
 
---
apiVersion: v1
kind: Service
metadata:
  name: shlink-redis-svc
  namespace: apps
spec:
  type: ClusterIP
  selector:
    app: shlink-redis
  ports:
    - name: redis
      port: 6379
      targetPort: 6379
 
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: shlink-redis
  namespace: apps
spec:
  replicas: 1
  selector:
    matchLabels:
      app: shlink-redis
  template:
    metadata:
      labels:
        app: shlink-redis
    spec:
      containers:
        - name: redis
          image: redis:7-alpine
          command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"]
          ports:
            - containerPort: 6379
          resources:
            requests:
              cpu: "100m"
              memory: "50Mi"
            limits:
              cpu: "0.5"
              memory: "256Mi"
          volumeMounts:
            - name: redis-data
              mountPath: /data
      volumes:
        - name: redis-data
          persistentVolumeClaim:
            claimName: shlink-redis-pvc

Shlink의 백엔드 설정입니다.

shlink.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: shlink-backend
  namespace: apps
spec:
  replicas: 1
  selector:
    matchLabels:
      app: shlink-backend
  template:
    metadata:
      labels:
        app: shlink-backend
    spec:
      containers:
        - name: backend
          image: shlinkio/shlink:latest
          ports:
            - containerPort: 8080
          envFrom:
            - configMapRef:
                name: shlink-config
          env:
            - name: DB_USER
              valueFrom:
                secretKeyRef:
                  name: shlink-secret
                  key: DB_USER
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: shlink-secret
                  key: DB_PASSWORD
            - name: DB_NAME
              valueFrom:
                secretKeyRef:
                  name: shlink-secret
                  key: DB_NAME
            - name: GEOLITE_LICENSE_KEY
              valueFrom:
                secretKeyRef:
                  name: shlink-secret
                  key: GEOLITE_LICENSE_KEY
          livenessProbe:
            httpGet:
              path: /rest/health
              port: 8080
            initialDelaySeconds: 20
            periodSeconds: 60
            timeoutSeconds: 10
            failureThreshold: 5
          resources:
            requests:
              cpu: "100m"
              memory: "500Mi"
            limits:
              cpu: "1.0"
              memory: "1.5Gi"

Shlink의 웹 설정입니다.

shilnk.yaml
apiVersion: v1
kind: Service
metadata:
  name: shlink-web-svc
  namespace: apps
spec:
  type: ClusterIP
  selector:
    app: shlink-web
  ports:
    - name: http
      port: 8080
      targetPort: 8080
 
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: shlink-web
  namespace: apps
spec:
  replicas: 1
  selector:
    matchLabels:
      app: shlink-web
  template:
    metadata:
      labels:
        app: shlink-web
    spec:
      containers:
        - name: web
          image: shlinkio/shlink-web-client:latest
          ports:
            - containerPort: 8080
          env:
            - name: TZ
              value: "Asia/Seoul"
            - name: SHLINK_SERVER_URL
              value: "https://link.junbeom.work" # Backend 주소
            - name: SHLINK_SERVER_API_KEY
              valueFrom:
                secretKeyRef:
                  name: shlink-secret
                  key: SHLINK_SERVER_API_KEY
          livenessProbe:
            httpGet:
              path: /
              port: 8080
            initialDelaySeconds: 20
            periodSeconds: 60
          resources:
            requests:
              cpu: "50m"
              memory: "20Mi"
            limits:
              cpu: "0.5"
              memory: "256Mi"

Backend Ingress

Shlink의 백엔드 담당 Ingress Controller 입니다. traefik을 사용해 SSL 인증서만 발급하여 모두가 볼 수 있습니다.

shlink.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shlink-backend-ingress
  namespace: apps
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-cloudflare"
spec:
  ingressClassName: traefik
  tls:
    - hosts:
        - link.junbeom.work    # 백엔드 도메인인
      secretName: shlink-backend-tls-secret
  rules:
    - host: link.junbeom.work  # 백엔드 도메인
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: shlink-backend-svc
                port:
                  number: 8080

Web Ingress

Shlink의 웹 담당 Ingress Controller 입니다. traefik을 이용한 SSL 발급과 내부망 제한 middleware를 적용하여 내부망에서만 접근 가능합니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shlink-web-ingress
  namespace: apps
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-cloudflare"
    traefik.ingress.kubernetes.io/router.middlewares: "apps-internal-only@kubernetescrd"
spec:
  ingressClassName: traefik
  tls:
    - hosts:
        - shlink.junbeom.work   # Web 도메인
      secretName: shlink-web-tls-secret
  rules:
    - host: shlink.junbeom.work # Web 도메인
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: shlink-web-svc
                port:
                  number: 8080

Tip

모든 객체는 shlink.yaml에 포함됩니다.

Installation

Secret 생성

shlink-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: shlink-secret
  namespace: apps
type: Opaque
data:
  DB_NAME: <DB_NAME>           
  DB_PASSWORD: <DB_PASSWORD>   
  DB_USER: <DB_USER>
  GEOLITE_LICENSE_KEY: <GEOLITE_LICENSE_KEY>
  SHLINK_SERVER_API_KEY: <SHLINK_SERVER_API_KEY>
  • kubectl apply -f shlink-secret.yaml

Danger

secret.yaml은 중요한 정보가 들어있으므로 보통 생성하고 파일을 삭제합니다.

Tip

SHLINK_SERVER_API_KEY는 서비스를 먼저 배포한 후에 발급 받아야 합니다.

GEOLITE_LICENSE_KEY

Shlink는 주소별 접근 데이터 분석을 지원하기 때문에 위치 데이터와 DB 접근이 필요합니다.

  1. Maxmind 접근
  2. 계정 생성 및 로그인
  3. My Account MANAGE LICENSE KEYS Generate new license key

배포

# shlink 배포
kubectl apply -f shlink.yaml
 
# API-KEY 생성
kubectl exec -it deployment/shlink-backend -n apps -- shlink api-key:generate

Success