개요
k8s에서 Ingress는 다양한 서비스 타입에게 트래픽을 전달할 때 자주 사용됩니다.
Ingress를 적용하기 위해서는 Ingress Controller가 사용되고 그 중 Nginx Ingress Controller를 통해 base url을 추가하는 방법에 대해 알아보려고 합니다.
base url이란?
먼저 base url에 대해 알아봅시다.
예를 들어 https://www.example.com이라는 사이트가 있다면 처음 접속했을 때 다음과 같은 html 파일을 받는다고 합시다.

여기서 url의 주소가 /static/image.png라면 html 파일을 렌더링 한 뒤 https://www.example.com/static/image.png로 요청을 보내 static 파일들을 다운받게 됩니다.
여기서 base url을 설정하게 될 경우 html 파일에 존재하는 여러 링크들에 prefix를 부여할 수 있습니다.
html 파일 상단에 다음과 같은 base tag를 작성해봅시다.
<base href="/prefix/">
이렇게 되면 주소가 /static/image.png 일 때 https://www.example.com/prefix/static/image.png로 요청을 보내게 됩니다.
base url의 필요성
그렇다면 이 base url은 언제 사용하면 좋을까요?
저는 Ingress Controller가 prefix 기반으로 트래픽을 라우팅 할 때 사용하고자 합니다.
k8s cluster 내부에서 argocd, nexus, harbor를 배포했을 때 서비스의 dashboard에 접근하기 위해 아래처럼 ingress 설정을 할 수 있습니다.

일반적인 백엔드 서비스를 배포할 때는 굳이 base url을 사용할 필요는 없겠지만 위 서비스들 같은 경우 dashboard가 존재하기 때문에 단순히 ingress만 사용한다면 문제가 발생할 수 있습니다.
이전 ArgoCD를 배포할 때도 발생한 문제입니다.
https://growth-coder.tistory.com/372
[k8s] argoCD 배포 총 정리
ArgoCD란?오늘은 k8s를 통해 서비스를 운영할 때 자동 배포 도구로 많이 사용하는 ArgoCD를 사용해보려고 합니다. ArgoCD란 k8s를 위한 선언적 GitOps 지속적 배포 도구입니다. Git 저장소에 정의된 애플리
growth-coder.tistory.com
ingress prefix를 "/argocd"로 둘 경우 dashboard에 접근하기 위해 html 파일을 받았을 때 "/static"으로 시작하는 url의 경우 "/argocd"로 향하지 않습니다.

하지만 여기서 만약 index.html 파일에서 base url이 설정되어 있었다면 추가 요청들에 prefix가 붙어 argocd로 향하게 만들 수 있습니다.

base url 설정 방법
base url을 설정하기 위해서는 전달받는 html 파일에 다음과 같은 base 태그를 추가해야 합니다.
<base href="/prefix/">
즉, index.html의 내용을 동적으로 변경해야 하는데 이는 nginx의 sub_filter 기능을 사용하여 구현할 수 있습니다.
sub filter 기능은 문자열을 치환하는 기능으로 head 태그 뒤에 base 태그를 추가하는 방식으로 구현할 수 있습니다.
그러나 저희는 k8s 위에 nginx를 직접 배포하여 관리하는 것이 아니라 Nginx Ingress Controller를 사용해서 간접적으로 배포해야 합니다.
이 때 nginx ingress에서 제공해주는 annotation을 사용하면 이 sub filter 기능을 구현할 수 있습니다.
과거에는 http://nginx.ingress.kubernetes.io/add-base-url을 제공했기 때문에 쉽게 구현할 수 있었으나 0.22.0 버전 이후 deprecated 되었습니다.
다음 PR에서 deprecated 되었다는 정보를 확인할 수 있었습니다.
https://github.com/kubernetes/ingress-nginx/pull/3174
Generalize Rewrite Block Creation and Deprecate AddBaseUrl (not backwards compatible) by zrdaley · Pull Request #3174 · kuber
What this PR does / why we need it: When the rewrite-target annotation is used and the written path uses regex and does not end in / (ex. foo/bar/.+), rewrite does not work as expected. This provid...
github.com
그래서 add-base-url annotation이 아닌 configuration-snippet annotation을 활용하려고 합니다.
ingress Object를 생성하면 nginx ingress controller가 ingress를 감지하여 nginx.conf에 location block을 추가하게 됩니다.
configuration-snippet은 nginx ingress controller가 nginx.conf에 생성한 location block에 원하는 custom 설정을 추가할 수 있습니다.
즉, configuration-snippet을 사용하면 location block에 sub_filter를 적용하여 base 태그를 적용할 수 있게 됩니다.
다만 일반적으로 nginx ingress controller에서는 configuration-snippet을 허용하지 않습니다.
ingress 생성 권한을 가지고 있는 누군가가 마음대로 html을 변경하여 악의적인 행동을 방지하기 위함입니다.
그래서 이 configuration-snippet을 사용하기 위해서는 다음 설정이 필요합니다.
- allow-snippet-annotation: "true"
- annotations-risk-level: Critical
configuration-snippet을 사용하는 것은 위험하기 때문에 risk level을 올리고 snippet-annotation을 허용해야 합니다.
다음 명령어를 통해 config map을 가져옵니다.
kubectl get configmap ingress-nginx-controller -n ingress-nginx -o yaml > nginx-config.yaml
만약 이름과 namespace가 다르다면 수정합니다.
다음과 같이 data.allow-snippet-annotation과 data.annotations-risk-level을 추가합니다.
apiVersion: v1
data:
allow-snippet-annotations: "true"
annotations-risk-level: Critical
kind: ConfigMap
...
바뀐 내용을 적용합니다.
kubectl apply -f ./nginx-config.yaml
이제 ingress에 configuration-snippet을 추가할 수 있습니다.
argocd-ingress를 다음과 같이 작성합니다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-ingress
namespace: argocd
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
nginx.ingress.kubernetes.io/configuration-snippet: |
sub_filter_types text/html;
sub_filter '<base href="/">' '';
sub_filter '<head>' '<head><base href="/argocd/">';
sub_filter_once on;
proxy_set_header Accept-Encoding "";
spec:
ingressClassName: nginx
rules:
- host: example.com
http:
paths:
- path: /argocd(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: argocd-server
port:
number: 443
configuration-snippet 설정은 다음과 같습니다.
- sub_filter_types text/html: html에만 적용합니다.
- sub_filter '<base href="/">' '': base url이 설정되어 있다면 제거합니다.
- sub_filter '<head>' '<head><base href="/argocd/">': head 태그를 찾아 뒤에 base 태그를 추가합니다.
- sub_filter_once on: 처음 찾은 head 태그에만 적용합니다.
- proxy_set_header Accept-Encoding "": 혹시 압축이 되어있다면 패턴을 못 찾을 수 있기 때문에 압축이 되지 않도록 설정합니다.
이제 html 파일의 url에 prefix를 지정하여 ingress controller가 요청을 제대로 전달할 수 있게 되었습니다.