Ingress nginx

svc를 ingress로 외부에 오픈

regex

https://kubernetes.github.io/ingress-nginx/user-guide/ingress-path-matching/

  • priority

    In NGINX, regular expressions follow a first match policy, 그러므로 ingress nginx가 정규식의 길이를 기준으로 역순으로 정렬을 한후 controller에 업데이트합니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress-1
spec:
  rules:
    - host: test.com
      http:
        paths:
          - path: /foo/bar
            backend:
              serviceName: service1
              servicePort: 80
          - path: /foo/bar/
            backend:
              serviceName: service2
              servicePort: 80
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress-2
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  rules:
    - host: test.com
      http:
        paths:
          - path: /foo/bar/(.+)
            backend:
              serviceName: service3
              servicePort: 80

이 같은 파일이 두개면 아래와 같이 정렬

location ~* ^/foo/bar/.+ {
  ...
}

location ~* "^/foo/bar/" {
  ...
}

location ~* "^/foo/bar" {
  ...
}
  • test.com/foo/bar/1 matches ~* ^/foo/bar/.+ and will go to service 3.

  • test.com/foo/bar/ matches ~* ^/foo/bar/ and will go to service 2.

  • test.com/foo/bar matches ~* ^/foo/bar and will go to service 1.

테스트를 위해 다음과같은 yaml을 만들어서 적용하엿다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress-1
  namespace: default
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
    - host: c4.aaaa.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: front
                port:
                  name: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress-2
  namespace: default
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  rules:
    - host: c4.aaaa.com
      http:
        paths:
          - path: /api(/|$)(.*)/i
            pathType: Prefix
            backend:
              service:
                name: api
                port:
                  name: http

결과 생성된 파일이다. 결론부터 이야기하면 ingress에 rewrite를 쓰면 그 인그레스에만 적용이 된다. 아래보면 /api부분에만 rewrite $1 가 적용되어 있다.

## start server c4.aaaa.com
server {
  server_name c4.aaaa.com ;

  listen 80  ;
  listen [::]:80  ;
  listen 443  ssl http2 ;
  listen [::]:443  ssl http2 ;

  set $proxy_upstream_name "-";

  ssl_certificate_by_lua_block {
    certificate.call()
  }

  location ~* "^/api(/|$)(.*)" {

    set $namespace      "default";
    set $ingress_name   "test-ingress-2";
    set $service_name   "api";
    set $service_port   "http";
    set $location_path  "/api(/|${literal_dollar})(.*)";
    set $global_rate_limit_exceeding n;

    rewrite_by_lua_block {
      lua_ingress.rewrite({
        force_ssl_redirect = false,
        ssl_redirect = true,
        force_no_ssl_redirect = false,
        preserve_trailing_slash = false,
        use_port_in_redirects = false,
        global_throttle = { namespace = "", limit = 0, window_size = 0, key = { }, ignored_cidrs = { } }
      })
      balancer.rewrite()
      plugins.run()
    }

    ...

    port_in_redirect off;

    set $balancer_ewma_score -1;
    set $proxy_upstream_name "default-api-http";
    set $proxy_host          $proxy_upstream_name;
    set $pass_access_scheme  $scheme;

    set $pass_server_port    $server_port;

    set $best_http_host      $http_host;
    set $pass_port           $pass_server_port;

    set $proxy_alternative_upstream_name "";

    client_max_body_size                    1m;

    proxy_set_header Host                   $best_http_host;

    # Pass the extracted client certificate to the backend

    # Allow websocket connections
    proxy_set_header                        Upgrade           $http_upgrade;

    proxy_set_header                        Connection        $connection_upgrade;

    proxy_set_header X-Request-ID           $req_id;
    proxy_set_header X-Real-IP              $remote_addr;

    proxy_set_header X-Forwarded-For        $remote_addr;

    proxy_set_header X-Forwarded-Host       $best_http_host;
    proxy_set_header X-Forwarded-Port       $pass_port;
    proxy_set_header X-Forwarded-Proto      $pass_access_scheme;
    proxy_set_header X-Forwarded-Scheme     $pass_access_scheme;

    proxy_set_header X-Scheme               $pass_access_scheme;

    # Pass the original X-Forwarded-For
    proxy_set_header X-Original-Forwarded-For $http_x_forwarded_for;

    # mitigate HTTPoxy Vulnerability
    # https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
    proxy_set_header Proxy                  "";
    # Custom headers to proxied server

    proxy_connect_timeout                   5s;
    proxy_send_timeout                      60s;
    proxy_read_timeout                      60s;

    proxy_buffering                         off;
    proxy_buffer_size                       4k;
    proxy_buffers                           4 4k;

    proxy_max_temp_file_size                1024m;

    proxy_request_buffering                 on;
    proxy_http_version                      1.1;

    proxy_cookie_domain                     off;
    proxy_cookie_path                       off;

    # In case of errors try the next upstream server before returning an error
    proxy_next_upstream                     error timeout;
    proxy_next_upstream_timeout             0;
    proxy_next_upstream_tries               3;

    rewrite "(?i)/api(/|$)(.*)" /$1 break;  # <------------여기
    proxy_pass http://upstream_balancer;

    proxy_redirect                          off;

  }

  location ~* "^/" {

    set $namespace      "default";
    set $ingress_name   "test-ingress-1";
    set $service_name   "front";
    set $service_port   "http";
    set $location_path  "/";
    set $global_rate_limit_exceeding n;

    rewrite_by_lua_block {
      lua_ingress.rewrite({
        force_ssl_redirect = false,
        ssl_redirect = true,
        force_no_ssl_redirect = false,
        preserve_trailing_slash = false,
        use_port_in_redirects = false,
        global_throttle = { namespace = "", limit = 0, window_size = 0, key = { }, ignored_cidrs = { } }
      })
      balancer.rewrite()
      plugins.run()
    }

    port_in_redirect off;

    set $balancer_ewma_score -1;
    set $proxy_upstream_name "default-front-http";
    set $proxy_host          $proxy_upstream_name;
    set $pass_access_scheme  $scheme;

    set $pass_server_port    $server_port;

    set $best_http_host      $http_host;
    set $pass_port           $pass_server_port;

    set $proxy_alternative_upstream_name "";

    client_max_body_size                    1m;

    proxy_set_header Host                   $best_http_host;

    # Pass the extracted client certificate to the backend

    # Allow websocket connections
    proxy_set_header                        Upgrade           $http_upgrade;

    proxy_set_header                        Connection        $connection_upgrade;

    proxy_set_header X-Request-ID           $req_id;
    proxy_set_header X-Real-IP              $remote_addr;

    proxy_set_header X-Forwarded-For        $remote_addr;

    proxy_set_header X-Forwarded-Host       $best_http_host;
    proxy_set_header X-Forwarded-Port       $pass_port;
    proxy_set_header X-Forwarded-Proto      $pass_access_scheme;
    proxy_set_header X-Forwarded-Scheme     $pass_access_scheme;

    proxy_set_header X-Scheme               $pass_access_scheme;

    # Pass the original X-Forwarded-For
    proxy_set_header X-Original-Forwarded-For $http_x_forwarded_for;

    # mitigate HTTPoxy Vulnerability
    # https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
    proxy_set_header Proxy                  "";

    # Custom headers to proxied server

    proxy_connect_timeout                   5s;
    proxy_send_timeout                      60s;
    proxy_read_timeout                      60s;

    proxy_buffering                         off;
    proxy_buffer_size                       4k;
    proxy_buffers                           4 4k;

    proxy_max_temp_file_size                1024m;

    proxy_request_buffering                 on;
    proxy_http_version                      1.1;

    proxy_cookie_domain                     off;
    proxy_cookie_path                       off;

    # In case of errors try the next upstream server before returning an error
    proxy_next_upstream                     error timeout;
    proxy_next_upstream_timeout             0;
    proxy_next_upstream_tries               3;

    proxy_pass http://upstream_balancer;

    proxy_redirect                          off;

  }

}
## end server c4.aaaa.com

주의사항

아래와 같이 만들면 문제가 잇을수 있다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress-3
  annotations:
    nginx.ingress.kubernetes.io/use-regex: 'true'
spec:
  rules:
    - host: test.com
      http:
        paths:
          - path: /foo/bar/bar
            backend:
              serviceName: test-a
              servicePort: 80
          - path: /foo/bar/[A-Z0-9]{3}
            backend:
              serviceName: test-b
              servicePort: 80

생성된 결과는 다음과 같다.

location ~* "^/foo/bar/[A-Z0-9]{3}" {
  ...
}
location ~* "^/foo/bar/bar" {
  ...
}

test.com/foo/bar/bar 이걸 요청하면 ^/foo/bar/[A-Z0-9]{3} 여기에 걸려버린다. 원하는대로 안된다.

더 알고 싶으면 다음 링크를 읽어보면된다.

https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms

log - 2022-09-11

로그를 커스터마이즈 하고 싶은 경우가 있다. 관련 도큐먼트는 다음에서 얻을수 있다.

log-format은 큰 도움이 안되었다. 그래서 2번을 참고해서 진행햇다.

configmap을 추가하면 되는것인데 helm을 사용하다보니 어디에 추가하는지 샘플이 없어서 고생햇다.

heml차트에 보면 다음 부분이 있다.

# Will add custom configuration options to Nginx https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/
# config: {} #이부분
config:
  enable-underscores-in-headers: on
  log-format-upstream: '$ingress_name "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_length $request_time [$proxy_upstream_name] [$proxy_alternative_upstream_name] $upstream_addr $upstream_response_length $upstream_response_time $upstream_status $req_id'

주석처리후 아래처럼 추가 configmap관련 내용을 추가햇다.

여기서 주의할건 log-format-upstream을 수정해야된다.

아래 보이는 log-format-stream을 가지고 아무리 해도 적용되지가 않앗다. log-format-upstream을 사용하는걸 보고 적용해보니 잘 되었다.

기본값은 다음과 같다.

$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_length $request_time [$proxy_upstream_name] [$proxy_alternative_upstream_name] $upstream_addr $upstream_response_length $upstream_response_time $upstream_status $req_id

나의 경우는 로그에 ingress name을 추가로 넣고 싶엇다. 확인해보니 1번 문서에서 추가로 사용할수 있다고 한다.

그래서 다음과 같이 추가햇다.

# Will add custom configuration options to Nginx https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/
config:
  enable-underscores-in-headers: on
  log-format-upstream: '$ingress_name "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_length $request_time [$proxy_upstream_name] [$proxy_alternative_upstream_name] $upstream_addr $upstream_response_length $upstream_response_time $upstream_status $req_id'

나의 경우는 고객 아이피는 중요한 상황이 아니고 로컬 시간도 중요치 않아서 제거하고 인그레스 이름을 맨 처음에 넣어주고 나머지는 기본값을 사용햇다.

그리고 로그를 확인해보니 다음과 같이 잘 나오는것을 확인햇다.

loki에서 확인해보기

이제 로그를 로키에서 확인해보자.

# 기존
{namespace=~"nginx"} | pattern `<remote_addr> - - <time_local> "<method> <_> <_>" <status> <_> <_> "<_>" <_>`  | status="400"

# 수정후
{namespace=~"nginx"} | pattern `<ingress> "<method> <_> <_>" <status> <_> <_> "<_>" <_>` | status=~"[4-5].*" | ingress="IngeressName"

이렇게 하면 ingress name별로 필터가 가능해진다.

grafana dash board

아래처럼 dashboard를 만들어 보앗다.

위에 두 그래프는 프로메테우스에서 메트릭을 가져와서 표현하고 마지막 테이블은 로키에서 로그를 가져와서 뿌려준다.

에러가 발생할때 이제 로그도 함께 볼수가 있어서 좋다.

데시보드 코드는 다음과 같다.

{
  "annotations": {
    "list": [
      {
        "builtIn": 1,
        "datasource": "-- Grafana --",
        "enable": true,
        "hide": true,
        "iconColor": "rgba(0, 211, 255, 1)",
        "name": "Annotations & Alerts",
        "target": {
          "limit": 100,
          "matchAny": false,
          "tags": [],
          "type": "dashboard"
        },
        "type": "dashboard"
      }
    ]
  },
  "editable": true,
  "fiscalYearStartMonth": 0,
  "graphTooltip": 0,
  "id": 35,
  "iteration": 1662994187189,
  "links": [],
  "liveNow": false,
  "panels": [
    {
      "aliasColors": {},
      "bars": false,
      "dashLength": 10,
      "dashes": false,
      "datasource": {
        "type": "prometheus",
        "uid": "P1809F7CD0C75ACF3"
      },
      "fill": 1,
      "fillGradient": 0,
      "gridPos": {
        "h": 8,
        "w": 24,
        "x": 0,
        "y": 0
      },
      "hiddenSeries": false,
      "id": 6,
      "legend": {
        "avg": false,
        "current": false,
        "max": false,
        "min": false,
        "show": false,
        "total": false,
        "values": false
      },
      "lines": true,
      "linewidth": 2,
      "nullPointMode": "connected",
      "options": {
        "alertThreshold": true
      },
      "percentage": false,
      "pluginVersion": "8.3.3",
      "pointradius": 2,
      "points": false,
      "renderer": "flot",
      "seriesOverrides": [],
      "spaceLength": 10,
      "stack": false,
      "steppedLine": false,
      "targets": [
        {
          "datasource": {
            "type": "prometheus",
            "uid": "P1809F7CD0C75ACF3"
          },
          "exemplar": true,
          "expr": "round(sum(irate(nginx_ingress_controller_requests{ingress=~\"$ingress\"}[2m])) by (ingress), 0.001)",
          "interval": "",
          "legendFormat": "",
          "refId": "A"
        }
      ],
      "thresholds": [],
      "timeRegions": [],
      "title": "Ingress Request Volume",
      "tooltip": {
        "shared": true,
        "sort": 2,
        "value_type": "individual"
      },
      "type": "graph",
      "xaxis": {
        "mode": "time",
        "show": true,
        "values": []
      },
      "yaxes": [
        {
          "$$hashKey": "object:57",
          "format": "reqps",
          "logBase": 1,
          "show": true
        },
        {
          "$$hashKey": "object:58",
          "format": "short",
          "logBase": 1,
          "show": false
        }
      ],
      "yaxis": {
        "align": false
      }
    },
    {
      "aliasColors": {},
      "bars": false,
      "dashLength": 10,
      "dashes": false,
      "datasource": {
        "type": "prometheus",
        "uid": "P1809F7CD0C75ACF3"
      },
      "fill": 1,
      "fillGradient": 0,
      "gridPos": {
        "h": 7,
        "w": 24,
        "x": 0,
        "y": 8
      },
      "hiddenSeries": false,
      "id": 4,
      "legend": {
        "avg": false,
        "current": false,
        "max": false,
        "min": false,
        "show": false,
        "total": false,
        "values": false
      },
      "lines": true,
      "linewidth": 2,
      "nullPointMode": "null",
      "options": {
        "alertThreshold": true
      },
      "percentage": false,
      "pluginVersion": "8.3.3",
      "pointradius": 2,
      "points": false,
      "renderer": "flot",
      "seriesOverrides": [],
      "spaceLength": 10,
      "stack": false,
      "steppedLine": false,
      "targets": [
        {
          "datasource": {
            "type": "prometheus",
            "uid": "P1809F7CD0C75ACF3"
          },
          "exemplar": true,
          "expr": "sum(rate(nginx_ingress_controller_requests{ingress=~\"$ingress\",status!~\"[4-5].*\"}[2m])) by (ingress) / sum(rate(nginx_ingress_controller_requests{ingress=~\"$ingress\"}[2m])) by (ingress)",
          "interval": "10",
          "legendFormat": "{{ingress}}",
          "refId": "A"
        }
      ],
      "thresholds": [],
      "timeRegions": [],
      "title": "Ingress Success Rate (non-4|5xx responses)",
      "tooltip": {
        "shared": true,
        "sort": 0,
        "value_type": "individual"
      },
      "type": "graph",
      "xaxis": {
        "mode": "time",
        "show": true,
        "values": []
      },
      "yaxes": [
        {
          "$$hashKey": "object:222",
          "format": "percentunit",
          "logBase": 1,
          "show": true
        },
        {
          "$$hashKey": "object:223",
          "format": "short",
          "logBase": 1,
          "show": false
        }
      ],
      "yaxis": {
        "align": false
      }
    },
    {
      "datasource": {
        "type": "loki",
        "uid": "P982945308D3682D1"
      },
      "gridPos": {
        "h": 14,
        "w": 24,
        "x": 0,
        "y": 15
      },
      "id": 2,
      "options": {
        "dedupStrategy": "none",
        "enableLogDetails": true,
        "prettifyLogMessage": false,
        "showCommonLabels": false,
        "showLabels": false,
        "showTime": false,
        "sortOrder": "Descending",
        "wrapLogMessage": false
      },
      "pluginVersion": "8.3.3",
      "targets": [
        {
          "datasource": {
            "type": "loki",
            "uid": "P982945308D3682D1"
          },
          "expr": "{namespace=~\"ingress-nginx|ingress-nginx-internal\"} | pattern `<ingress> \"<method> <_> <_>\" <status> <_> <_> \"<_>\" <_>`| status=~\"[4-5].*\" | ingress=\"$ingress\"",
          "instant": false,
          "range": true,
          "refId": "log"
        }
      ],
      "title": "Http 4-5xx",
      "type": "logs"
    }
  ],
  "schemaVersion": 34,
  "style": "dark",
  "tags": [],
  "templating": {
    "list": [
      {
        "current": {
          "selected": false,
          "text": "shop-ingress",
          "value": "shop-ingress"
        },
        "datasource": {
          "type": "prometheus",
          "uid": "P1809F7CD0C75ACF3"
        },
        "definition": "label_values(ingress) ",
        "hide": 0,
        "includeAll": true,
        "label": "ingress",
        "multi": false,
        "name": "ingress",
        "options": [],
        "query": {
          "query": "label_values(ingress) ",
          "refId": "StandardVariableQuery"
        },
        "refresh": 1,
        "regex": "",
        "skipUrlSync": false,
        "sort": 2,
        "type": "query"
      }
    ]
  },
  "time": {
    "from": "now-12h",
    "to": "now"
  },
  "timepicker": {},
  "timezone": "",
  "title": "Loggging",
  "uid": "A-3q5wMVz",
  "version": 9,
  "weekStart": ""
}

log-format vs log-format-upstream

아직 잘 모르는데 이 다이어그램이 좀 도움이 되는듯.

이걸 한번 잘 읽어봐야겟다. https://www.nginx.com/blog/logging-upstream-nginx-traffic-cdn77/ 그나저나 ingress-nginx는 왜 upstream로그를 기본으로 사용할가? 프록시니까 맞는것도 같고...

helmchart 사용중 configmap설정

helmchart를 사용중에 configmap을 설정해야하는 경우가 있다. 매뉴얼등이 전부 configmap만 나오면 다음처럼 하자.

helm chart 에 config에 설정을 넣으면 된다.

# Will add custom configuration options to Nginx https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/
config:
  enable-underscores-in-headers: on

여기에 사용할수 있는 항목들은 다음 페이지에서 확인한다.

https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#configuration-options

꽤 많은 옵션들이 여기에서 처리할수 있다.

values 파일을 변경후 argocd에서 로딩을 하거나 적용을 하면 pod에 자동으로 config가 reload가 된다. 중요한것은 pod가 재시작 되지 않는다. 설정만 자동으로 리로드 된다.

만약 설정이 잘못되면 test fail 나면서 리로딩 하지 않는다.

todo

  • log-format-upstream 과 log-format의 차이점 파악하기

  • dashboard를 kube-prometheus에서 넣어줘야한다.

Last updated

Was this helpful?