mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-05 17:16:59 +02:00
chore: merge with master
This commit is contained in:
commit
56773c0430
8
.github/dependabot.yml
vendored
8
.github/dependabot.yml
vendored
@ -9,3 +9,11 @@ updates:
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "docker" # Keep Docker dependencies up to date
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
22
.github/workflows/ci.yml
vendored
22
.github/workflows/ci.yml
vendored
@ -6,40 +6,38 @@ on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
checks: write # to create a new check based on the results (shogo82148/actions-goveralls)
|
||||
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ^1.17
|
||||
go-version: 1.19
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install CI
|
||||
run: |
|
||||
go get -v -t -d ./...
|
||||
if [ -f Gopkg.toml ]; then
|
||||
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||
dep ensure
|
||||
fi
|
||||
|
||||
- name: Install additional CI for nektos/act
|
||||
run: |
|
||||
apt update
|
||||
apt install -y make gcc libc-dev git
|
||||
if: github.actor == 'nektos/act'
|
||||
|
||||
- name: Lint
|
||||
run: |
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.30.0
|
||||
make lint
|
||||
|
||||
- name: Test
|
||||
run: make test
|
||||
|
10
.github/workflows/codeql-analysis.yml
vendored
10
.github/workflows/codeql-analysis.yml
vendored
@ -26,15 +26,15 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: Install go version
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '^1.16'
|
||||
go-version: '^1.19'
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@ -46,4 +46,4 @@ jobs:
|
||||
make build
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
47
.github/workflows/docs.yml
vendored
Normal file
47
.github/workflows/docs.yml
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
name: Release Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
release_docs:
|
||||
permissions:
|
||||
contents: write # for mike to push
|
||||
|
||||
name: Release Docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
cache: "pip"
|
||||
cache-dependency-path: "./docs/scripts/requirements.txt"
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ^1.19
|
||||
|
||||
- run: |
|
||||
pip install -r docs/scripts/requirements.txt
|
||||
|
||||
- name: setup
|
||||
run: |
|
||||
./docs/scripts/copy_docs.sh
|
||||
go run ./docs/scripts/docs.go
|
||||
|
||||
- name: Configure Git user
|
||||
run: |
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
|
||||
- name: build and push
|
||||
run: |
|
||||
mike deploy ${{ github.ref_name }} latest --push --update-aliases
|
||||
mike set-default --push latest
|
41
.github/workflows/gh-workflow-approve.yaml
vendored
Normal file
41
.github/workflows/gh-workflow-approve.yaml
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
name: Approve GH Workflows
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- labeled
|
||||
- synchronize
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
approve:
|
||||
name: Approve ok-to-test
|
||||
if: contains(github.event.pull_request.labels.*.name, 'ok-to-test')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: write
|
||||
steps:
|
||||
- name: Update PR
|
||||
uses: actions/github-script@d556feaca394842dc55e4734bf3bb9f685482fa0 # v6.3.3
|
||||
continue-on-error: true
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
debug: ${{ secrets.ACTIONS_RUNNER_DEBUG }}
|
||||
script: |
|
||||
const result = await github.rest.actions.listWorkflowRunsForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
event: "pull_request",
|
||||
status: "action_required",
|
||||
head_sha: context.payload.pull_request.head.sha,
|
||||
per_page: 100
|
||||
});
|
||||
|
||||
for (var run of result.data.workflow_runs) {
|
||||
await github.rest.actions.approveWorkflowRun({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: run.id
|
||||
});
|
||||
}
|
23
.github/workflows/json-yaml-validate.yml
vendored
Normal file
23
.github/workflows/json-yaml-validate.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
name: json-yaml-validate
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write # enable write permissions for pull requests
|
||||
|
||||
jobs:
|
||||
json-yaml-validate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: json-yaml-validate
|
||||
uses: GrantBirki/json-yaml-validate@v1.2.0
|
||||
with:
|
||||
comment: "true" # enable comment mode
|
||||
yaml_exclude_regex: "(charts/external-dns/templates.*|mkdocs.yml)"
|
36
.github/workflows/lint-test-chart.yaml
vendored
36
.github/workflows/lint-test-chart.yaml
vendored
@ -9,24 +9,36 @@ jobs:
|
||||
lint-test:
|
||||
if: github.repository == 'kubernetes-sigs/external-dns'
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@v1
|
||||
with:
|
||||
version: v3.6.3
|
||||
- name: Run Artifact Hub lint
|
||||
run: |
|
||||
set -euo pipefail
|
||||
curl -Lo ah_linux_amd64.tar.gz https://github.com/artifacthub/hub/releases/download/v1.9.0/ah_1.9.0_linux_amd64.tar.gz
|
||||
tar -xzvf ah_linux_amd64.tar.gz ah
|
||||
./ah lint --kind helm || exit 1
|
||||
rm -f ./ah ./ah_linux_amd64.tar.gz
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
- name: Set-up Helm
|
||||
uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78
|
||||
with:
|
||||
python-version: 3.7
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: latest
|
||||
|
||||
- name: Set up chart-testing
|
||||
uses: helm/chart-testing-action@v2.1.0
|
||||
- name: Set-up Python
|
||||
uses: actions/setup-python@b55428b1882923874294fa556849718a1d7f2ca5
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
- name: Set-up chart-testing
|
||||
uses: helm/chart-testing-action@afea100a513515fbd68b0e72a7bb0ae34cb62aec
|
||||
|
||||
- name: Run chart-testing (list-changed)
|
||||
id: list-changed
|
||||
@ -39,8 +51,8 @@ jobs:
|
||||
- name: Run chart-testing (lint)
|
||||
run: ct lint --check-version-increment=false
|
||||
|
||||
- name: Create Kind cluster
|
||||
uses: helm/kind-action@v1.2.0
|
||||
- name: Set-up Kind cluster
|
||||
uses: helm/kind-action@d8ccf8fb623ce1bb360ae2f45f323d9d5c5e9f00
|
||||
with:
|
||||
wait: 120s
|
||||
if: steps.list-changed.outputs.changed == 'true'
|
||||
|
35
.github/workflows/lint.yaml
vendored
Normal file
35
.github/workflows/lint.yaml
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
checks: write # to create a new check based on the results (shogo82148/actions-goveralls)
|
||||
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.19
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Lint
|
||||
run: |
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.50.1
|
||||
make lint
|
41
.github/workflows/release-chart.yaml
vendored
41
.github/workflows/release-chart.yaml
vendored
@ -7,28 +7,59 @@ on:
|
||||
paths:
|
||||
- "charts/external-dns/Chart.yaml"
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
release:
|
||||
permissions:
|
||||
contents: write # to push chart release and create a release (helm/chart-releaser-action)
|
||||
|
||||
if: github.repository == 'kubernetes-sigs/external-dns'
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get chart version
|
||||
id: chart_version
|
||||
run: |
|
||||
set -euo pipefail
|
||||
chart_version="$(grep -Po "(?<=^version: ).+" charts/external-dns/Chart.yaml)"
|
||||
echo "::set-output name=version::${chart_version}"
|
||||
|
||||
- name: Get changelog entry
|
||||
id: changelog_reader
|
||||
uses: mindsers/changelog-reader-action@b97ce03a10d9bdbb07beb491c76a5a01d78cd3ef
|
||||
with:
|
||||
path: charts/external-dns/CHANGELOG.md
|
||||
version: "v${{ steps.chart_version.outputs.version }}"
|
||||
|
||||
- name: Create release notes
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cat <<"EOF" > charts/external-dns/RELEASE.md
|
||||
${{ steps.changelog_reader.outputs.changes }}
|
||||
EOF
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config user.name "$GITHUB_ACTOR"
|
||||
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
|
||||
|
||||
- name: Install Helm
|
||||
uses: azure/setup-helm@v1
|
||||
- name: Set-up Helm
|
||||
uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78
|
||||
with:
|
||||
version: v3.6.3
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: latest
|
||||
|
||||
- name: Run chart-releaser
|
||||
uses: helm/chart-releaser-action@v1.2.1
|
||||
uses: helm/chart-releaser-action@be16258da8010256c6e82849661221415f031968
|
||||
env:
|
||||
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
CR_RELEASE_NAME_TEMPLATE: "external-dns-helm-chart-{{ .Version }}"
|
||||
CR_RELEASE_NOTES_FILE: RELEASE.md
|
||||
CR_MAKE_RELEASE_LATEST: false
|
||||
|
37
.github/workflows/staging-image-tester.yml
vendored
Normal file
37
.github/workflows/staging-image-tester.yml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
name: Build all images
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
checks: write # to create a new check based on the results (shogo82148/actions-goveralls)
|
||||
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.19
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install CI
|
||||
run: |
|
||||
go get -v -t -d ./...
|
||||
|
||||
- name: Test
|
||||
run: make build.image/multiarch
|
6
.github/workflows/trivy.yml
vendored
6
.github/workflows/trivy.yml
vendored
@ -1,13 +1,17 @@
|
||||
name: trivy vulnerability scanner
|
||||
on:
|
||||
push:
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: Build an image from Dockerfile
|
||||
run: |
|
||||
make build.docker
|
||||
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -54,3 +54,10 @@ profile.cov
|
||||
|
||||
# Helm charts
|
||||
!/charts/external-dns/
|
||||
|
||||
docs/LICENSE.md
|
||||
docs/code-of-conduct.md
|
||||
docs/CONTRIBUTING.md
|
||||
docs/index.md
|
||||
docs/redirect
|
||||
site
|
@ -3,39 +3,38 @@ linters-settings:
|
||||
default-signifies-exhaustive: false
|
||||
goimports:
|
||||
local-prefixes: sigs.k8s.io/external-dns
|
||||
golint:
|
||||
min-confidence: 0.9
|
||||
maligned:
|
||||
suggest-new: true
|
||||
misspell:
|
||||
locale: US
|
||||
revive:
|
||||
confusing-naming: false
|
||||
ignore-generated-header: true
|
||||
rules:
|
||||
- name: confusing-naming
|
||||
disabled: true
|
||||
|
||||
linters:
|
||||
# please, do not use `enable-all`: it's deprecated and will be removed soon.
|
||||
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
|
||||
disable-all: true
|
||||
enable:
|
||||
- deadcode
|
||||
- depguard
|
||||
- dogsled
|
||||
- gofmt
|
||||
- goimports
|
||||
- golint
|
||||
- goprintffuncname
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- interfacer
|
||||
- misspell
|
||||
- rowserrcheck
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- stylecheck
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unused
|
||||
- varcheck
|
||||
- whitespace
|
||||
- revive
|
||||
- unused
|
||||
- gosimple
|
||||
- staticcheck
|
||||
|
||||
issues:
|
||||
# Excluding configuration per-path, per-linter, per-text and per-source
|
||||
@ -47,12 +46,10 @@ issues:
|
||||
- dogsled
|
||||
- gofmt
|
||||
- goimports
|
||||
- golint
|
||||
- goprintffuncname
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- interfacer
|
||||
- misspell
|
||||
- nolintlint
|
||||
- rowserrcheck
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
# builder image
|
||||
ARG ARCH
|
||||
FROM golang:1.17 as builder
|
||||
FROM golang:1.19 as builder
|
||||
ARG ARCH
|
||||
|
||||
WORKDIR /sigs.k8s.io/external-dns
|
||||
@ -24,10 +24,10 @@ COPY go.sum .
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
RUN make test build.$ARCH
|
||||
|
||||
# final image
|
||||
FROM $ARCH/alpine:3.15
|
||||
FROM alpine:3.17
|
||||
|
||||
RUN apk update && apk add "libcrypto3>=3.0.8-r1" "libssl3>=3.0.8-r1" && rm -rf /var/cache/apt/*
|
||||
|
||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
COPY --from=builder /sigs.k8s.io/external-dns/build/external-dns /bin/external-dns
|
||||
|
@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM golang:1.17 as builder
|
||||
FROM golang:1.19 as builder
|
||||
|
||||
WORKDIR /sigs.k8s.io/external-dns
|
||||
|
||||
|
79
Makefile
79
Makefile
@ -19,7 +19,7 @@
|
||||
cover:
|
||||
go get github.com/wadey/gocovmerge
|
||||
$(eval PKGS := $(shell go list ./... | grep -v /vendor/))
|
||||
$(eval PKGS_DELIM := $(shell echo $(PKGS) | sed -e 's/ /,/g'))
|
||||
$(eval PKGS_DELIM := $(shell echo $(PKGS) | tr / -'))
|
||||
go list -f '{{if or (len .TestGoFiles) (len .XTestGoFiles)}}go test -test.v -test.timeout=120s -covermode=count -coverprofile={{.Name}}_{{len .Imports}}_{{len .Deps}}.coverprofile -coverpkg $(PKGS_DELIM) {{.ImportPath}}{{end}}' $(PKGS) | xargs -0 sh -c
|
||||
gocovmerge `ls *.coverprofile` > cover.out
|
||||
rm *.coverprofile
|
||||
@ -90,8 +90,11 @@ IMAGE ?= us.gcr.io/k8s-artifacts-prod/external-dns/$(BINARY)
|
||||
VERSION ?= $(shell git describe --tags --always --dirty)
|
||||
BUILD_FLAGS ?= -v
|
||||
LDFLAGS ?= -X sigs.k8s.io/external-dns/pkg/apis/externaldns.Version=$(VERSION) -w -s
|
||||
ARCHS = amd64 arm64v8 arm32v7
|
||||
SHELL = /bin/bash
|
||||
ARCHS = amd64 arm64 arm/v7
|
||||
ARCH ?= amd64
|
||||
DEFAULT_ARCH = amd64
|
||||
SHELL = /bin/bash
|
||||
OUTPUT_TYPE ?= docker
|
||||
|
||||
|
||||
build: build/$(BINARY)
|
||||
@ -99,37 +102,65 @@ build: build/$(BINARY)
|
||||
build/$(BINARY): $(SOURCES)
|
||||
CGO_ENABLED=0 go build -o build/$(BINARY) $(BUILD_FLAGS) -ldflags "$(LDFLAGS)" .
|
||||
|
||||
build.push/multiarch:
|
||||
build.push/multiarch: $(addprefix build.push-,$(ARCHS))
|
||||
arch_specific_tags=()
|
||||
for arch in $(ARCHS); do \
|
||||
image="$(IMAGE):$(VERSION)-$${arch}" ;\
|
||||
# pre-pull due to https://github.com/kubernetes-sigs/cluster-addons/pull/84/files ;\
|
||||
docker pull $${arch}/alpine:3.14 ;\
|
||||
docker pull golang:1.17 ;\
|
||||
DOCKER_BUILDKIT=1 docker build --rm --tag $${image} --build-arg VERSION="$(VERSION)" --build-arg ARCH="$${arch}" . ;\
|
||||
docker push $${image} ;\
|
||||
arch_specific_tags+=( "--amend $${image}" ) ;\
|
||||
image="$(IMAGE):$(VERSION)-$$(echo $$arch | tr / -)" ;\
|
||||
arch_specific_tags+=( " $${image}" ) ;\
|
||||
done ;\
|
||||
DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create "$(IMAGE):$(VERSION)" $${arch_specific_tags[@]} ;\
|
||||
for arch in $(ARCHS); do \
|
||||
DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate --arch $${arch} "$(IMAGE):$(VERSION)" "$(IMAGE):$(VERSION)-$${arch}" ;\
|
||||
done;\
|
||||
DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(IMAGE):$(VERSION)" \
|
||||
echo $${arch_specific_tags[@]} ;\
|
||||
DOCKER_CLI_EXPERIMENTAL=enabled docker buildx imagetools create --tag "$(IMAGE):$(VERSION)" $${arch_specific_tags[@]} ;\
|
||||
|
||||
build.push: build.docker
|
||||
docker push "$(IMAGE):$(VERSION)"
|
||||
build.image/multiarch: $(addprefix build.image-,$(ARCHS))
|
||||
|
||||
build.arm64v8:
|
||||
build.image:
|
||||
$(MAKE) ARCH=$(ARCH) OUTPUT_TYPE=docker build.docker
|
||||
|
||||
build.image-amd64:
|
||||
$(MAKE) ARCH=amd64 build.image
|
||||
|
||||
build.image-arm64:
|
||||
$(MAKE) ARCH=arm64 build.image
|
||||
|
||||
build.image-arm/v7:
|
||||
$(MAKE) ARCH=arm/v7 build.image
|
||||
|
||||
build.push:
|
||||
$(MAKE) ARCH=$(ARCH) OUTPUT_TYPE=registry build.docker
|
||||
|
||||
build.push-amd64:
|
||||
$(MAKE) ARCH=amd64 build.push
|
||||
|
||||
build.push-arm64:
|
||||
$(MAKE) ARCH=arm64 build.push
|
||||
|
||||
build.push-arm/v7:
|
||||
$(MAKE) ARCH=arm/v7 build.push
|
||||
|
||||
build.arm64:
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o build/$(BINARY) $(BUILD_FLAGS) -ldflags "$(LDFLAGS)" .
|
||||
|
||||
build.amd64:
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o build/$(BINARY) $(BUILD_FLAGS) -ldflags "$(LDFLAGS)" .
|
||||
|
||||
build.arm32v7:
|
||||
build.arm/v7:
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -o build/$(BINARY) $(BUILD_FLAGS) -ldflags "$(LDFLAGS)" .
|
||||
|
||||
build.docker:
|
||||
docker build --rm --tag "$(IMAGE):$(VERSION)" --build-arg VERSION="$(VERSION)" --build-arg ARCH="amd64" .
|
||||
build.setup:
|
||||
docker buildx inspect img-builder > /dev/null || docker buildx create --name img-builder --use
|
||||
|
||||
build.docker: build.setup build.$(ARCH)
|
||||
docker build --rm --tag "$(IMAGE):$(VERSION)" --build-arg VERSION="$(VERSION)" --build-arg ARCH="$(ARCH)" .
|
||||
image="$(IMAGE):$(VERSION)-$(subst /,-,$(ARCH))"; \
|
||||
docker buildx build \
|
||||
--pull \
|
||||
--provenance=false \
|
||||
--sbom=false \
|
||||
--output=type=$(OUTPUT_TYPE) \
|
||||
--platform linux/$(ARCH) \
|
||||
--build-arg ARCH="$(ARCH)" \
|
||||
--build-arg VERSION="$(VERSION)" \
|
||||
--tag $${image} .
|
||||
|
||||
build.mini:
|
||||
docker build --rm --tag "$(IMAGE):$(VERSION)-mini" --build-arg VERSION="$(VERSION)" -f Dockerfile.mini .
|
||||
@ -140,8 +171,8 @@ clean:
|
||||
# Builds and push container images to the staging bucket.
|
||||
.PHONY: release.staging
|
||||
|
||||
release.staging:
|
||||
release.staging: test
|
||||
IMAGE=$(IMAGE_STAGING) $(MAKE) build.push/multiarch
|
||||
|
||||
release.prod:
|
||||
release.prod: test
|
||||
$(MAKE) build.push/multiarch
|
||||
|
2
OWNERS
2
OWNERS
@ -5,11 +5,13 @@ approvers:
|
||||
- raffo
|
||||
- njuettner
|
||||
- seanmalloy
|
||||
- szuecs
|
||||
|
||||
reviewers:
|
||||
- njuettner
|
||||
- raffo
|
||||
- seanmalloy
|
||||
- szuecs
|
||||
|
||||
emeritus_approvers:
|
||||
- hjacobs
|
||||
|
42
README.md
42
README.md
@ -1,13 +1,17 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
- navigation
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<img src="img/external-dns.png" width="40%" align="center" alt="ExternalDNS">
|
||||
</p>
|
||||
|
||||
# ExternalDNS
|
||||
[](https://github.com/kubernetes-sigs/external-dns/actions)
|
||||
[](https://coveralls.io/github/kubernetes-sigs/external-dns)
|
||||
[](https://github.com/kubernetes-sigs/external-dns/releases)
|
||||
[](https://godoc.org/github.com/kubernetes-sigs/external-dns)
|
||||
[](https://goreportcard.com/report/github.com/kubernetes-sigs/external-dns)
|
||||
|
||||
[](https://github.com/kubernetes-sigs/external-dns/actions) [](https://coveralls.io/github/kubernetes-sigs/external-dns) [](https://github.com/kubernetes-sigs/external-dns/releases) [](https://godoc.org/github.com/kubernetes-sigs/external-dns) [](https://goreportcard.com/report/github.com/kubernetes-sigs/external-dns) [](https://kubernetes-sigs.github.io/external-dns/)
|
||||
|
||||
|
||||
ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers.
|
||||
|
||||
@ -23,16 +27,16 @@ To see ExternalDNS in action, have a look at this [video](https://www.youtube.co
|
||||
|
||||
## The Latest Release
|
||||
|
||||
ExternalDNS' allows you to keep selected zones (via `--domain-filter`) synchronized with Ingresses and Services of `type=LoadBalancer` in various cloud providers:
|
||||
ExternalDNS allows you to keep selected zones (via `--domain-filter`) synchronized with Ingresses and Services of `type=LoadBalancer` and nodes in various DNS providers:
|
||||
* [Google Cloud DNS](https://cloud.google.com/dns/docs/)
|
||||
* [AWS Route 53](https://aws.amazon.com/route53/)
|
||||
* [AWS Cloud Map](https://docs.aws.amazon.com/cloud-map/)
|
||||
* [AzureDNS](https://azure.microsoft.com/en-us/services/dns)
|
||||
* [BlueCat](https://bluecatnetworks.com)
|
||||
* [Civo](https://www.civo.com)
|
||||
* [CloudFlare](https://www.cloudflare.com/dns)
|
||||
* [RcodeZero](https://www.rcodezero.at/)
|
||||
* [DigitalOcean](https://www.digitalocean.com/products/networking)
|
||||
* [Hetzner](https://hetzner.com/)
|
||||
* [DNSimple](https://dnsimple.com/)
|
||||
* [Infoblox](https://www.infoblox.com/products/dns/)
|
||||
* [Dyn](https://dyn.com/dns/)
|
||||
@ -52,7 +56,12 @@ ExternalDNS' allows you to keep selected zones (via `--domain-filter`) synchroni
|
||||
* [Akamai Edge DNS](https://learn.akamai.com/en-us/products/cloud_security/edge_dns.html)
|
||||
* [GoDaddy](https://www.godaddy.com)
|
||||
* [Gandi](https://www.gandi.net)
|
||||
* [UKFast SafeDNS](https://my.ukfast.co.uk/safedns/)
|
||||
* [ANS Group SafeDNS](https://portal.ans.co.uk/safedns/)
|
||||
* [IBM Cloud DNS](https://www.ibm.com/cloud/dns)
|
||||
* [TencentCloud PrivateDNS](https://cloud.tencent.com/product/privatedns)
|
||||
* [TencentCloud DNSPod](https://cloud.tencent.com/product/cns)
|
||||
* [Plural](https://www.plural.sh/)
|
||||
* [Pi-hole](https://pi-hole.net/)
|
||||
|
||||
From this release, ExternalDNS can become aware of the records it is managing (enabled via `--registry=txt`), therefore ExternalDNS can safely manage non-empty hosted zones. We strongly encourage you to use `v0.5` (or greater) with `--registry=txt` enabled and `--txt-owner-id` set to a unique value that doesn't change for the lifetime of your cluster. You might also want to run ExternalDNS in a dry run mode (`--dry-run` flag) to see the changes to be submitted to your DNS Provider API.
|
||||
|
||||
@ -86,10 +95,10 @@ The following table clarifies the current status of the providers according to t
|
||||
| Akamai Edge DNS | Beta | |
|
||||
| AzureDNS | Beta | |
|
||||
| BlueCat | Alpha | @seanmalloy @vinny-sabatini |
|
||||
| Civo | Alpha | @alejandrojnm |
|
||||
| CloudFlare | Beta | |
|
||||
| RcodeZero | Alpha | |
|
||||
| DigitalOcean | Alpha | |
|
||||
| Hetzner | Alpha | @21h |
|
||||
| DNSimple | Alpha | |
|
||||
| Infoblox | Alpha | @saileshgiri |
|
||||
| Dyn | Alpha | |
|
||||
@ -111,6 +120,10 @@ The following table clarifies the current status of the providers according to t
|
||||
| GoDaddy | Alpha | |
|
||||
| Gandi | Alpha | @packi |
|
||||
| SafeDNS | Alpha | @assureddt |
|
||||
| IBMCloud | Alpha | @hughhuangzh |
|
||||
| TencentCloud | Alpha | @Hyzhou |
|
||||
| Plural | Alpha | @michaeljguarino |
|
||||
| Pi-hole | Alpha | @tinyzimmer |
|
||||
|
||||
## Kubernetes version compatibility
|
||||
|
||||
@ -136,18 +149,18 @@ The following tutorials are provided:
|
||||
* [Akamai Edge DNS](docs/tutorials/akamai-edgedns.md)
|
||||
* [Alibaba Cloud](docs/tutorials/alibabacloud.md)
|
||||
* AWS
|
||||
* [ALB Ingress Controller](docs/tutorials/alb-ingress.md)
|
||||
* [AWS Load Balancer Controller](docs/tutorials/aws-load-balancer-controller.md)
|
||||
* [Route53](docs/tutorials/aws.md)
|
||||
* [Same domain for public and private Route53 zones](docs/tutorials/public-private-route53.md)
|
||||
* [Cloud Map](docs/tutorials/aws-sd.md)
|
||||
* [Kube Ingress AWS Controller](docs/tutorials/kube-ingress-aws.md)
|
||||
* [Azure DNS](docs/tutorials/azure.md)
|
||||
* [Azure Private DNS](docs/tutorials/azure-private-dns.md)
|
||||
* [Civo](docs/tutorials/civo.md)
|
||||
* [Cloudflare](docs/tutorials/cloudflare.md)
|
||||
* [BlueCat](docs/tutorials/bluecat.md)
|
||||
* [CoreDNS](docs/tutorials/coredns.md)
|
||||
* [DigitalOcean](docs/tutorials/digitalocean.md)
|
||||
* [Hetzner](docs/tutorials/hetzner.md)
|
||||
* [DNSimple](docs/tutorials/dnsimple.md)
|
||||
* [Dyn](docs/tutorials/dyn.md)
|
||||
* [Exoscale](docs/tutorials/exoscale.md)
|
||||
@ -177,7 +190,12 @@ The following tutorials are provided:
|
||||
* [UltraDNS](docs/tutorials/ultradns.md)
|
||||
* [GoDaddy](docs/tutorials/godaddy.md)
|
||||
* [Gandi](docs/tutorials/gandi.md)
|
||||
* [SafeDNS](docs/tutorials/safedns.md)
|
||||
* [SafeDNS](docs/tutorials/UKFast_SafeDNS.md)
|
||||
* [IBM Cloud](docs/tutorials/ibmcloud.md)
|
||||
* [Nodes as source](docs/tutorials/nodes.md)
|
||||
* [TencentCloud](docs/tutorials/tencentcloud.md)
|
||||
* [Plural](docs/tutorials/plural.md)
|
||||
* [Pi-hole](docs/tutorials/pihole.md)
|
||||
|
||||
### Running Locally
|
||||
|
||||
|
88
charts/external-dns/CHANGELOG.md
Normal file
88
charts/external-dns/CHANGELOG.md
Normal file
@ -0,0 +1,88 @@
|
||||
# ExternalDNS Helm Chart Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
---
|
||||
|
||||
## [UNRELEASED]
|
||||
|
||||
### All Changes
|
||||
|
||||
## [v1.12.2] - UNRELEASED
|
||||
|
||||
### All Changes
|
||||
|
||||
- Added support for ServiceMonitor relabelling. ([#3366](https://github.com/kubernetes-sigs/external-dns/pull/3366)) [@jkroepke](https://github.com/jkroepke)
|
||||
- Updated chart icon path. ([#3492](https://github.com/kubernetes-sigs/external-dns/pull/3494)) [kundan2707](https://github.com/kundan2707)
|
||||
- Added RBAC for Gateway-API resources to ClusterRole. ([#3499](https://github.com/kubernetes-sigs/external-dns/pull/3499)) [@michaelvl](https://github.com/MichaelVL)
|
||||
- Added RBAC for F5 VirtualServer to ClusterRole. ([#3503](https://github.com/kubernetes-sigs/external-dns/pull/3503)) [@mikejoh](https://github.com/mikejoh)
|
||||
- Added support for running ExternalDNS with namespaced scope. ([#3403](https://github.com/kubernetes-sigs/external-dns/pull/3403)) [@jkroepke](https://github.com/jkroepke)
|
||||
- Updated _ExternalDNS_ version to [v0.13.4](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.13.4). ([#3516](https://github.com/kubernetes-sigs/external-dns/pull/3516)) [@stevehipwell](https://github.com/stevehipwell)
|
||||
|
||||
## [v1.12.1] - 2023-02-06
|
||||
|
||||
### All Changes
|
||||
|
||||
- Updated _ExternalDNS_ version to [v0.13.2](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.13.2). ([#3371](https://github.com/kubernetes-sigs/external-dns/pull/3371)) [@stevehipwell](https://github.com/stevehipwell)
|
||||
- Added `secretConfiguration.subPath` to mount specific files from secret as a sub-path. ([#3227](https://github.com/kubernetes-sigs/external-dns/pull/3227)) [@jkroepke](https://github.com/jkroepke)
|
||||
- Changed to use `registry.k8s.io` instead of `k8s.gcr.io`. ([#3261](https://github.com/kubernetes-sigs/external-dns/pull/3261)) [@johngmyers](https://github.com/johngmyers)
|
||||
|
||||
## [v1.12.0] - 2022-11-29
|
||||
|
||||
### All Changes
|
||||
|
||||
- Added ability to provide ExternalDNS with secret configuration via `secretConfiguration`. ([#3144](https://github.com/kubernetes-sigs/external-dns/pull/3144)) [@jkroepke](https://github.com/jkroepke)
|
||||
- Added the ability to template `provider` & `extraArgs`. ([#3144](https://github.com/kubernetes-sigs/external-dns/pull/3144)) [@jkroepke](https://github.com/jkroepke)
|
||||
- Added the ability to customise the service account labels. ([#3145](https://github.com/kubernetes-sigs/external-dns/pull/3145)) [@jkroepke](https://github.com/jkroepke)
|
||||
- Updated _ExternalDNS_ version to [v0.13.1](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.13.1). ([#3197](https://github.com/kubernetes-sigs/external-dns/pull/3197)) [@stevehipwell](https://github.com/stevehipwell)
|
||||
|
||||
## [v1.11.0] - 2022-08-10
|
||||
|
||||
### Added
|
||||
|
||||
- Added support to configure `dnsPolicy` on the Helm chart deployment. [@michelzanini](https://github.com/michelzanini)
|
||||
- Added ability to customise the deployment strategy. [mac-chaffee](https://github.com/mac-chaffee)
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated _ExternalDNS_ version to [v0.12.2](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.12.2). [@stevehipwell](https://github.com/stevehipwell)
|
||||
- Changed default deployment strategy to `Recreate`. [mac-chaffee](https://github.com/mac-chaffee)
|
||||
|
||||
## [v1.10.1] - 2022-07-11
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed incorrect addition of `namespace` to `ClusterRole` & `ClusterRoleBinding`. [@stevehipwell](https://github.com/stevehipwell)
|
||||
|
||||
## [v1.10.0] - 2022-07-08
|
||||
|
||||
### Added
|
||||
|
||||
- Added `commonLabels` value to allow the addition of labels to all resources. [@stevehipwell](https://github.com/stevehipwell)
|
||||
- Added support for [Process Namespace Sharing](https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/) via the `shareProcessNamespace`
|
||||
value. ([#2715](https://github.com/kubernetes-sigs/external-dns/pull/2715)) [@wolffberg](https://github.com/wolffberg)
|
||||
|
||||
### Changed
|
||||
|
||||
- Update _ExternalDNS_ version to [v0.12.0](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.12.0). [@vojtechmares](https://github.com/vojtechmares)
|
||||
- Set resource namespaces to `{{ .Release.Namespace }}` in the templates instead of waiting until apply time for inference. [@stevehipwell](https://github.com/stevehipwell)
|
||||
- Fixed `rbac.additionalPermissions` default value.([#2796](https://github.com/kubernetes-sigs/external-dns/pull/2796)) [@tamalsaha](https://github.com/tamalsaha)
|
||||
|
||||
## [v1.9.0] - 2022-04-19
|
||||
|
||||
### Changed
|
||||
|
||||
- Update _ExternalDNS_ version to [v0.11.0](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.11.0). ([#2690](https://github.com/kubernetes-sigs/external-dns/pull/2690)) [@stevehipwell](https://github.com/stevehipwell)
|
||||
|
||||
## [v1.8.0] - 2022-04-13
|
||||
|
||||
### Added
|
||||
|
||||
- Add annotations to Deployment. ([#2477](https://github.com/kubernetes-sigs/external-dns/pull/2477)) [@beastob](https://github.com/beastob)
|
||||
|
||||
### Changed
|
||||
|
||||
- Fix RBAC for `istio-virtualservice` source when `istio-gateway` isn't also added. ([#2564](https://github.com/kubernetes-sigs/external-dns/pull/2564)) [@mcwarman](https://github.com/mcwarman)
|
@ -2,14 +2,17 @@ apiVersion: v2
|
||||
name: external-dns
|
||||
description: ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers.
|
||||
type: application
|
||||
version: 1.7.1
|
||||
appVersion: 0.10.2
|
||||
version: 1.12.2
|
||||
appVersion: 0.13.4
|
||||
keywords:
|
||||
- kubernetes
|
||||
- externaldns
|
||||
- external-dns
|
||||
- dns
|
||||
- service
|
||||
- ingress
|
||||
home: https://github.com/kubernetes-sigs/external-dns/
|
||||
icon: https://github.com/kubernetes-sigs/external-dns/raw/master/img/external-dns.png
|
||||
icon: https://github.com/kubernetes-sigs/external-dns/raw/master/docs/img/external-dns.png
|
||||
sources:
|
||||
- https://github.com/kubernetes-sigs/external-dns/
|
||||
maintainers:
|
||||
@ -18,8 +21,14 @@ maintainers:
|
||||
annotations:
|
||||
artifacthub.io/changes: |
|
||||
- kind: added
|
||||
description: "Allow custom ClusterRole rules to be specified for sources without defaults."
|
||||
description: "Added support for ServiceMonitor relabelling."
|
||||
- kind: changed
|
||||
description: "Update ExternalDNS version to v0.10.2."
|
||||
description: "Updated chart icon path."
|
||||
- kind: added
|
||||
description: "Added RBAC for Gateway-API resources to ClusterRole."
|
||||
- kind: added
|
||||
description: "Added RBAC for F5 VirtualServer to ClusterRole."
|
||||
- kind: added
|
||||
description: "Added support for running ExternalDNS with namespaced scope."
|
||||
- kind: changed
|
||||
description: "Set ClusterRole rules based more enabled sources."
|
||||
description: "Updated _ExternalDNS_ version to [v0.13.4](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.13.4)."
|
||||
|
@ -13,58 +13,106 @@ helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
|
||||
After you've installed the repo you can install the chart.
|
||||
|
||||
```shell
|
||||
helm upgrade --install external-dns/external-dns
|
||||
helm upgrade --install external-dns external-dns/external-dns
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The following table lists the configurable parameters of the _ExternalDNS_ chart and their default values.
|
||||
|
||||
| Parameter | Description | Default |
|
||||
|-----------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------|
|
||||
| `image.repository` | Image repository. | `k8s.gcr.io/external-dns/external-dns` |
|
||||
| `image.tag` | Image tag, will override the default tag derived from the chart app version. | `""` |
|
||||
| `image.pullPolicy` | Image pull policy. | `IfNotPresent` |
|
||||
| `imagePullSecrets` | Image pull secrets. | `[]` |
|
||||
| `nameOverride` | Override the `name` of the chart. | `""` |
|
||||
| `fullnameOverride` | Override the `fullname` of the chart. | `""` |
|
||||
| `serviceAccount.create` | If `true`, create a new `serviceaccount`. | `true` |
|
||||
| `serviceAccount.annotations` | Annotations to add to the service account. | `{}` |
|
||||
| `serviceAccount.name` | Service account to be used. If not set and `serviceAccount.create` is `true`, a name is generated using the full name template. | `""` |
|
||||
| `rbac.create` | If `true`, create the RBAC resources. | `true` |
|
||||
| `rbac.additionalPermissions` | Additional permissions to be added to the cluster role. | `{}` |
|
||||
| `deploymentAnnotations` | Annotations to add to the Deployment. | `{}` |
|
||||
| `podLabels` | Labels to add to the pod. | `{}` |
|
||||
| `podAnnotations` | Annotations to add to the pod. | `{}` |
|
||||
| `podSecurityContext` | Security context for the pod, this supports the full [PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#podsecuritycontext-v1-core) API. | _see values.yaml_ |
|
||||
| `securityContext` | Security context for the _external-dns_ container, this supports the full [SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#securitycontext-v1-core) API. | _see values.yaml_ |
|
||||
| `priorityClassName` | Priority class name to use for the pod. | `""` |
|
||||
| `terminationGracePeriodSeconds` | Termination grace period for the pod. | `null` |
|
||||
| `serviceMonitor.enabled` | If `true`, create a _Prometheus_ service monitor. | `false` |
|
||||
| `serviceMonitor.additionalLabels` | Additional labels to be set on the ServiceMonitor. | `{}` |
|
||||
| `serviceMonitor.interval` | _Prometheus_ scrape frequency. | `1m` |
|
||||
| `serviceMonitor.scrapeTimeout` | _Prometheus_ scrape timeout. | `10s` |
|
||||
| `env` | [Environment variables](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) for the _external-dns_ container, this supports the full [EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#envvar-v1-core) API including secrets and configmaps. | `[]` |
|
||||
| `livenessProbe` | [Liveness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) for the _external-dns_ container, this supports the full [Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#probe-v1-core) API. | See _values.yaml_ |
|
||||
| `readinessProbe` | [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) for the _external-dns_ container, this supports the full [Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#probe-v1-core) API. | See _values.yaml_ |
|
||||
| `service.annotations` | Annotations to add to the service. | `{}` |
|
||||
| `service.port` | Port to expose via the service. | `7979` |
|
||||
| `extraVolumes` | Additional volumes for the pod, this supports the full [VolumeDevice](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#volumedevice-v1-core) API. | `[]` |
|
||||
| `extraVolumeMounts` | Additional volume mounts for the _external-dns_ container, this supports the full [VolumeMount](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#volumemount-v1-core) API. | `[]` |
|
||||
| `resources` | Resource requests and limits for the _external-dns_ container, this supports the full [ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#resourcerequirements-v1-core) API. | `{}` |
|
||||
| `nodeSelector` | Node labels for pod assignment. | `{}` |
|
||||
| `tolerations` | Tolerations for pod assignment, this supports the full [Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#toleration-v1-core) API. | `[]` |
|
||||
| `affinity` | Affinity settings for pod assignment, this supports the full [Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#affinity-v1-core) API. | `{}` |
|
||||
| `topologySpreadConstraints` | TopologySpreadConstraint settings for pod assignment, this supports the full [TopologySpreadConstraints](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#topologyspreadconstraint-v1-core) API. | `[]` |
|
||||
| `logLevel` | Verbosity of the logs, available values are: `panic`, `debug`, `info`, `warn`, `error`, `fatal`. | `info` |
|
||||
| `logFormat` | Formats of the logs, available values are: `text`, `json`. | `text` |
|
||||
| `interval` | The interval for DNS updates. | `1m` |
|
||||
| `triggerLoopOnEvent` | When enabled, triggers run loop on create/update/delete events in addition of regular interval. | `false` |
|
||||
| `sources` | K8s resources type to be observed for new DNS entries. | See _values.yaml_ |
|
||||
| `policy` | How DNS records are synchronized between sources and providers, available values are: `sync`, `upsert-only`. | `upsert-only` |
|
||||
| `registry` | Registry Type, available types are: `txt`, `noop`. | `txt` |
|
||||
| `txtOwnerId` | TXT registry identifier. | `""` |
|
||||
| `txtPrefix` | Prefix to create a TXT record with a name following the pattern `prefix.<CNAME record>`. | `""` |
|
||||
| `domainFilters` | Limit possible target zones by domain suffixes. | `[]` |
|
||||
| `provider` | DNS provider where the DNS records will be created, for the available providers and how to configure them see the [README](https://github.com/kubernetes-sigs/external-dns#deploying-to-a-cluster). | `aws` |
|
||||
| `extraArgs` | Extra arguments to pass to the _external-dns_ container, these are needed for provider specific arguments. | `[]` |
|
||||
| Parameter | Description | Default |
|
||||
|------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------|
|
||||
| `image.repository` | Image repository. | `registry.k8s.io/external-dns/external-dns` |
|
||||
| `image.tag` | Image tag, will override the default tag derived from the chart app version. | `""` |
|
||||
| `image.pullPolicy` | Image pull policy. | `IfNotPresent` |
|
||||
| `imagePullSecrets` | Image pull secrets. | `[]` |
|
||||
| `nameOverride` | Override the `name` of the chart. | `""` |
|
||||
| `fullnameOverride` | Override the `fullname` of the chart. | `""` |
|
||||
| `serviceAccount.create` | If `true`, create a new `serviceaccount`. | `true` |
|
||||
| `serviceAccount.annotations` | Annotations to add to the service account. | `{}` |
|
||||
| `serviceAccount.labels` | Labels to add to the service account. | `{}` |
|
||||
| `serviceAccount.name` | Service account to be used. If not set and `serviceAccount.create` is `true`, a name is generated using the full name template. | `""` |
|
||||
| `rbac.create` | If `true`, create the RBAC resources. | `true` |
|
||||
| `rbac.additionalPermissions` | Additional permissions to be added to the cluster role. | `{}` |
|
||||
| `deploymentAnnotations` | Annotations to add to the Deployment. | `{}` |
|
||||
| `podLabels` | Labels to add to the pod. | `{}` |
|
||||
| `podAnnotations` | Annotations to add to the pod. | `{}` |
|
||||
| `podSecurityContext` | Security context for the pod, this supports the full [PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#podsecuritycontext-v1-core) API. | _see values.yaml_ |
|
||||
| `shareProcessNamespace` | If `true` enable [Process Namespace Sharing](https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/) | `false` |
|
||||
| `securityContext` | Security context for the _external-dns_ container, this supports the full [SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#securitycontext-v1-core) API. | _see values.yaml_ |
|
||||
| `priorityClassName` | Priority class name to use for the pod. | `""` |
|
||||
| `terminationGracePeriodSeconds` | Termination grace period for the pod. | `null` |
|
||||
| `serviceMonitor.enabled` | If `true`, create a _Prometheus_ service monitor. | `false` |
|
||||
| `serviceMonitor.namespace` | Forced namespace for ServiceMonitor. | `null` |
|
||||
| `serviceMonitor.annotations` | Annotations to be set on the ServiceMonitor. | `{}` |
|
||||
| `serviceMonitor.additionalLabels` | Additional labels to be set on the ServiceMonitor. | `{}` |
|
||||
| `serviceMonitor.interval` | _Prometheus_ scrape frequency. | `null` |
|
||||
| `serviceMonitor.scrapeTimeout` | _Prometheus_ scrape timeout. | `null` |
|
||||
| `serviceMonitor.scheme` | _Prometheus_ scrape scheme. | `null` |
|
||||
| `serviceMonitor.tlsConfig` | _Prometheus_ scrape tlsConfig. | `{}` |
|
||||
| `serviceMonitor.metricRelabelings` | _Prometheus_ scrape metricRelabelings. | `[]` |
|
||||
| `serviceMonitor.relabelings` | _Prometheus_ scrape relabelings. | `[]` |
|
||||
| `serviceMonitor.targetLabels` | _Prometheus_ scrape targetLabels. | `[]` |
|
||||
| `env` | [Environment variables](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) for the _external-dns_ container, this supports the full [EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#envvar-v1-core) API including secrets and configmaps. | `[]` |
|
||||
| `livenessProbe` | [Liveness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) for the _external-dns_ container, this supports the full [Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#probe-v1-core) API. | See _values.yaml_ |
|
||||
| `readinessProbe` | [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) for the _external-dns_ container, this supports the full [Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#probe-v1-core) API. | See _values.yaml_ |
|
||||
| `service.annotations` | Annotations to add to the service. | `{}` |
|
||||
| `service.port` | Port to expose via the service. | `7979` |
|
||||
| `extraVolumes` | Additional volumes for the pod, this supports the full [VolumeDevice](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#volumedevice-v1-core) API. | `[]` |
|
||||
| `extraVolumeMounts` | Additional volume mounts for the _external-dns_ container, this supports the full [VolumeMount](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#volumemount-v1-core) API. | `[]` |
|
||||
| `resources` | Resource requests and limits for the _external-dns_ container, this supports the full [ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#resourcerequirements-v1-core) API. | `{}` |
|
||||
| `nodeSelector` | Node labels for pod assignment. | `{}` |
|
||||
| `tolerations` | Tolerations for pod assignment, this supports the full [Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#toleration-v1-core) API. | `[]` |
|
||||
| `affinity` | Affinity settings for pod assignment, this supports the full [Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#affinity-v1-core) API. | `{}` |
|
||||
| `topologySpreadConstraints` | TopologySpreadConstraint settings for pod assignment, this supports the full [TopologySpreadConstraints](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#topologyspreadconstraint-v1-core) API. | `[]` |
|
||||
| `logLevel` | Verbosity of the logs, available values are: `panic`, `debug`, `info`, `warning`, `error`, `fatal`. | `info` |
|
||||
| `logFormat` | Formats of the logs, available values are: `text`, `json`. | `text` |
|
||||
| `interval` | The interval for DNS updates. | `1m` |
|
||||
| `triggerLoopOnEvent` | When enabled, triggers run loop on create/update/delete events in addition of regular interval. | `false` |
|
||||
| `namespaced` | When enabled, external-dns runs on namespace scope. Additionally, Role and Rolebinding will be namespaced, too. | `false` |
|
||||
| `sources` | K8s resources type to be observed for new DNS entries. | See _values.yaml_ |
|
||||
| `policy` | How DNS records are synchronized between sources and providers, available values are: `sync`, `upsert-only`. | `upsert-only` |
|
||||
| `registry` | Registry Type, available types are: `txt`, `noop`. | `txt` |
|
||||
| `txtOwnerId` | TXT registry identifier. | `""` |
|
||||
| `txtPrefix` | Prefix to create a TXT record with a name following the pattern `prefix.<CNAME record>`. | `""` |
|
||||
| `domainFilters` | Limit possible target zones by domain suffixes. | `[]` |
|
||||
| `provider` | DNS provider where the DNS records will be created, for the available providers and how to configure them see the [README](https://github.com/kubernetes-sigs/external-dns#deploying-to-a-cluster) (this can be templated). | `aws` |
|
||||
| `extraArgs` | Extra arguments to pass to the _external-dns_ container, these are needed for provider specific arguments (these can be templated). | `[]` |
|
||||
| `deploymentStrategy` | .spec.strategy of the external-dns Deployment. Defaults to 'Recreate' since multiple external-dns pods may conflict with each other. | `{type: Recreate}` |
|
||||
| `secretConfiguration.enabled` | Enable additional secret configuration. | `false` |
|
||||
| `secretConfiguration.mountPath` | Mount path of secret configuration secret (this can be templated). | `""` |
|
||||
| `secretConfiguration.data` | Secret configuration secret data. Could be used to store DNS provider credentials. | `{}` |
|
||||
| `secretConfiguration.subPath` | Sub-path of secret configuration secret (this can be templated). | `""` |
|
||||
|
||||
## Namespaced scoped installation
|
||||
|
||||
external-dns supports running on a namespaced only scope, too.
|
||||
If `namespaced=true` is defined, the helm chart will setup `Roles` and `RoleBindings` instead `ClusterRoles` and `ClusterRoleBindings`.
|
||||
|
||||
### Limited supported
|
||||
Not all sources are supported in namespaced scope, since some sources depends on cluster-wide resources.
|
||||
For example: Source `node` isn't supported, since `kind: Node` has scope `Cluster`.
|
||||
Sources like `istio-virtualservice` only work, if all resources like `Gateway` and `VirtualService` are present in the same
|
||||
namespaces as `external-dns`.
|
||||
|
||||
The annotation `external-dns.alpha.kubernetes.io/endpoints-type: NodeExternalIP` is not supported.
|
||||
|
||||
If `namespaced` is set to `true`, please ensure that `sources` my only contains supported sources (Default: `service,ingress`.
|
||||
|
||||
### Support matrix
|
||||
|
||||
| Source | Supported | Infos |
|
||||
|------------------------|-----------|------------------------|
|
||||
| `ingress` | ✅ | |
|
||||
| `istio-gateway` | ✅ | |
|
||||
| `istio-virtualservice` | ✅ | |
|
||||
| `contour-ingressroute` | ✅ | |
|
||||
| `crd` | ✅ | |
|
||||
| `kong-tcpingress` | ✅ | |
|
||||
| `openshift-route` | ✅ | |
|
||||
| `skipper-routegroup` | ✅ | |
|
||||
| `gloo-proxy` | ✅ | |
|
||||
| `contour-httpproxy` | ✅ | |
|
||||
| `service` | ⚠️️ | NodePort not supported |
|
||||
| `node` | ❌ | |
|
||||
| `pod` | ❌ | |
|
||||
|
@ -40,6 +40,9 @@ helm.sh/chart: {{ include "external-dns.chart" . }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- with .Values.commonLabels }}
|
||||
{{ toYaml . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
|
@ -1,35 +1,31 @@
|
||||
{{- if .Values.rbac.create -}}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
kind: {{ .Values.namespaced | ternary "Role" "ClusterRole" }}
|
||||
metadata:
|
||||
name: {{ template "external-dns.fullname" . }}
|
||||
labels:
|
||||
{{- include "external-dns.labels" . | nindent 4 }}
|
||||
rules:
|
||||
{{- if or (has "node" .Values.sources) (has "pod" .Values.sources) (has "service" .Values.sources) (has "contour-httpproxy" .Values.sources) (has "gloo-proxy" .Values.sources) (has "openshift-route" .Values.sources) (has "skipper-routegroup" .Values.sources) }}
|
||||
{{- if and (not .Values.namespaced) (or (has "node" .Values.sources) (has "pod" .Values.sources) (has "service" .Values.sources) (has "contour-httpproxy" .Values.sources) (has "gloo-proxy" .Values.sources) (has "openshift-route" .Values.sources) (has "skipper-routegroup" .Values.sources)) }}
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list","watch"]
|
||||
{{- end }}
|
||||
|
||||
{{- if or (has "pod" .Values.sources) (has "service" .Values.sources) (has "contour-httpproxy" .Values.sources) (has "gloo-proxy" .Values.sources) (has "openshift-route" .Values.sources) (has "skipper-routegroup" .Values.sources) }}
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if or (has "service" .Values.sources) (has "contour-httpproxy" .Values.sources) (has "gloo-proxy" .Values.sources) (has "istio-gateway" .Values.sources) (has "istio-virtualservice" .Values.sources) (has "openshift-route" .Values.sources) (has "skipper-routegroup" .Values.sources) }}
|
||||
- apiGroups: [""]
|
||||
resources: ["services","endpoints"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if or (has "ingress" .Values.sources) (has "contour-httpproxy" .Values.sources) (has "openshift-route" .Values.sources) (has "skipper-routegroup" .Values.sources) }}
|
||||
- apiGroups: ["extensions","networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if or (has "istio-gateway" .Values.sources) (has "istio-virtualservice" .Values.sources) }}
|
||||
- apiGroups: ["networking.istio.io"]
|
||||
resources: ["gateways"]
|
||||
@ -41,25 +37,21 @@ rules:
|
||||
resources: ["virtualservices"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "ambassador-host" .Values.sources }}
|
||||
- apiGroups: ["getambassador.io"]
|
||||
resources: ["hosts","ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "contour-httpproxy" .Values.sources }}
|
||||
- apiGroups: ["projectcontour.io"]
|
||||
resources: ["httpproxies"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "contour-ingressroute" .Values.sources }}
|
||||
- apiGroups: ["contour.heptio.com"]
|
||||
resources: ["ingressroutes"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "crd" .Values.sources }}
|
||||
- apiGroups: ["externaldns.k8s.io"]
|
||||
resources: ["dnsendpoints"]
|
||||
@ -68,25 +60,56 @@ rules:
|
||||
resources: ["dnsendpoints/status"]
|
||||
verbs: ["*"]
|
||||
{{- end }}
|
||||
|
||||
{{- if or (has "gateway-httproute" .Values.sources) (has "gateway-grpcroute" .Values.sources) (has "gateway-tlsroute" .Values.sources) (has "gateway-tcproute" .Values.sources) (has "gateway-udproute" .Values.sources) }}
|
||||
- apiGroups: ["gateway.networking.k8s.io"]
|
||||
resources: ["gateways"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
{{- if has "gateway-httproute" .Values.sources }}
|
||||
- apiGroups: ["gateway.networking.k8s.io"]
|
||||
resources: ["httproutes"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
{{- if has "gateway-httproute" .Values.sources }}
|
||||
- apiGroups: [""]
|
||||
resources: ["namespaces"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
{{- if has "gateway-grpcroute" .Values.sources }}
|
||||
- apiGroups: ["gateway.networking.k8s.io"]
|
||||
resources: ["grpcroutes"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
{{- if has "gateway-tlsroute" .Values.sources }}
|
||||
- apiGroups: ["gateway.networking.k8s.io"]
|
||||
resources: ["tlsroutes"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
{{- if has "gateway-tcproute" .Values.sources }}
|
||||
- apiGroups: ["gateway.networking.k8s.io"]
|
||||
resources: ["tcproutes"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
{{- if has "gateway-udproute" .Values.sources }}
|
||||
- apiGroups: ["gateway.networking.k8s.io"]
|
||||
resources: ["udproutes"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
{{- if has "gloo-proxy" .Values.sources }}
|
||||
- apiGroups: ["gloo.solo.io","gateway.solo.io"]
|
||||
resources: ["proxies","virtualservices"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "kong-tcpingress" .Values.sources }}
|
||||
- apiGroups: ["configuration.konghq.com"]
|
||||
resources: ["tcpingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "openshift-route" .Values.sources }}
|
||||
- apiGroups: ["route.openshift.io"]
|
||||
resources: ["routes"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "skipper-routegroup" .Values.sources }}
|
||||
- apiGroups: ["zalando.org"]
|
||||
resources: ["routegroups"]
|
||||
@ -95,9 +118,12 @@ rules:
|
||||
resources: ["routegroups/status"]
|
||||
verbs: ["patch","update"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "f5-virtualserver" .Values.sources }}
|
||||
- apiGroups: ["cis.f5.com"]
|
||||
resources: ["virtualservers"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
{{- with .Values.rbac.additionalPermissions }}
|
||||
{{- toYaml . | nindent 2 }}
|
||||
{{- end }}
|
||||
|
||||
{{- end }}
|
||||
|
@ -1,13 +1,13 @@
|
||||
{{- if .Values.rbac.create -}}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
kind: {{ .Values.namespaced | ternary "RoleBinding" "ClusterRoleBinding" }}
|
||||
metadata:
|
||||
name: {{ printf "%s-viewer" (include "external-dns.fullname" .) }}
|
||||
labels:
|
||||
{{- include "external-dns.labels" . | nindent 4 }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
kind: {{ .Values.namespaced | ternary "Role" "ClusterRole" }}
|
||||
name: {{ template "external-dns.fullname" . }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
|
@ -2,6 +2,7 @@ apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "external-dns.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "external-dns.labels" . | nindent 4 }}
|
||||
{{- with .Values.deploymentAnnotations }}
|
||||
@ -13,6 +14,8 @@ spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "external-dns.selectorLabels" . | nindent 6 }}
|
||||
strategy:
|
||||
{{- toYaml .Values.deploymentStrategy | nindent 4 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
@ -20,9 +23,14 @@ spec:
|
||||
{{- with .Values.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.podAnnotations }}
|
||||
{{- if or .Values.secretConfiguration.enabled .Values.podAnnotations }}
|
||||
annotations:
|
||||
{{- if .Values.secretConfiguration.enabled }}
|
||||
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
|
||||
{{- end }}
|
||||
{{- with .Values.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
@ -30,6 +38,9 @@ spec:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "external-dns.serviceAccountName" . }}
|
||||
{{- with .Values.shareProcessNamespace }}
|
||||
shareProcessNamespace: {{ . }}
|
||||
{{- end }}
|
||||
{{- with .Values.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
@ -40,6 +51,9 @@ spec:
|
||||
{{- with .Values.terminationGracePeriodSeconds }}
|
||||
terminationGracePeriodSeconds: {{ . }}
|
||||
{{- end }}
|
||||
{{- with .Values.dnsPolicy }}
|
||||
dnsPolicy: {{ . }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: external-dns
|
||||
{{- with .Values.securityContext }}
|
||||
@ -75,12 +89,15 @@ spec:
|
||||
- --txt-suffix={{ .Values.txtSuffix }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.namespaced }}
|
||||
- --namespace={{ .Release.Namespace }}
|
||||
{{- end }}
|
||||
{{- range .Values.domainFilters }}
|
||||
- --domain-filter={{ . }}
|
||||
{{- end }}
|
||||
- --provider={{ .Values.provider }}
|
||||
- --provider={{ tpl .Values.provider $ }}
|
||||
{{- range .Values.extraArgs }}
|
||||
- {{ . }}
|
||||
- {{ tpl . $ }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: http
|
||||
@ -90,17 +107,33 @@ spec:
|
||||
{{- toYaml .Values.livenessProbe | nindent 12 }}
|
||||
readinessProbe:
|
||||
{{- toYaml .Values.readinessProbe | nindent 12 }}
|
||||
{{- with .Values.extraVolumeMounts }}
|
||||
{{- if or .Values.secretConfiguration.enabled .Values.extraVolumeMounts }}
|
||||
volumeMounts:
|
||||
{{- if .Values.secretConfiguration.enabled }}
|
||||
- name: secrets
|
||||
mountPath: {{ tpl .Values.secretConfiguration.mountPath $ }}
|
||||
{{- with .Values.secretConfiguration.subPath }}
|
||||
subPath: {{ tpl . $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.extraVolumeMounts }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.extraVolumes }}
|
||||
{{- if or .Values.secretConfiguration.enabled .Values.extraVolumes }}
|
||||
volumes:
|
||||
{{- if .Values.secretConfiguration.enabled }}
|
||||
- name: secrets
|
||||
secret:
|
||||
secretName: {{ include "external-dns.fullname" . }}
|
||||
{{- end }}
|
||||
{{- with .Values.extraVolumes }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
|
13
charts/external-dns/templates/secret.yaml
Normal file
13
charts/external-dns/templates/secret.yaml
Normal file
@ -0,0 +1,13 @@
|
||||
{{- if .Values.secretConfiguration.enabled }}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "external-dns.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "external-dns.labels" . | nindent 4 }}
|
||||
data:
|
||||
{{- range $key, $value := .Values.secretConfiguration.data }}
|
||||
{{ $key }}: {{ tpl $value $ | b64enc | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
@ -2,6 +2,7 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "external-dns.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "external-dns.labels" . | nindent 4 }}
|
||||
{{- with .Values.service.annotations }}
|
||||
|
@ -3,8 +3,12 @@ apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "external-dns.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "external-dns.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.labels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
|
@ -1,13 +1,18 @@
|
||||
{{- if.Values.serviceMonitor.enabled -}}
|
||||
{{- if .Values.serviceMonitor.enabled -}}
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: {{ include "external-dns.fullname" . }}
|
||||
namespace: {{ default .Release.Namespace .Values.serviceMonitor.namespace }}
|
||||
{{- with .Values.serviceMonitor.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "external-dns.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceMonitor.additionalLabels }}
|
||||
{{- with .Values.serviceMonitor.additionalLabels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
spec:
|
||||
jobLabel: {{ .Release.Name }}
|
||||
namespaceSelector:
|
||||
@ -22,7 +27,29 @@ spec:
|
||||
{{- with .Values.serviceMonitor.interval }}
|
||||
interval: {{ . }}
|
||||
{{- end }}
|
||||
{{- with .Values.serviceMonitor.scrapeTimeout }}
|
||||
{{- with .Values.serviceMonitor.scheme }}
|
||||
scheme: {{ . }}
|
||||
{{- end }}
|
||||
{{- with .Values.serviceMonitor.bearerTokenFile }}
|
||||
bearerTokenFile: {{ . }}
|
||||
{{- end }}
|
||||
{{- with .Values.serviceMonitor.tlsConfig }}
|
||||
tlsConfig:
|
||||
{{- toYaml .| nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.serviceMonitor.scrapeTimeout }}
|
||||
scrapeTimeout: {{ . }}
|
||||
{{- end }}
|
||||
{{- with .Values.serviceMonitor.metricRelabelings }}
|
||||
metricRelabelings:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.serviceMonitor.relabelings }}
|
||||
relabelings:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.serviceMonitor.targetLabels }}
|
||||
targetLabels:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
31
charts/external-dns/values.schema.json
Normal file
31
charts/external-dns/values.schema.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"provider": {
|
||||
"type": "string"
|
||||
},
|
||||
"extraArgs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"secretConfiguration": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mountPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
".+": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,20 +3,25 @@
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
image:
|
||||
repository: k8s.gcr.io/external-dns/external-dns
|
||||
repository: registry.k8s.io/external-dns/external-dns
|
||||
# Overrides the image tag whose default is v{{ .Chart.AppVersion }}
|
||||
tag: ""
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
imagePullSecrets: []
|
||||
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
commonLabels: {}
|
||||
|
||||
serviceAccount:
|
||||
# Specifies whether a service account should be created
|
||||
create: true
|
||||
# Annotations to add to the service account
|
||||
annotations: {}
|
||||
# Labels to add to the service account
|
||||
labels: {}
|
||||
# The name of the service account to use.
|
||||
# If not set and create is true, a name is generated using the fullname template
|
||||
name: ""
|
||||
@ -24,7 +29,7 @@ serviceAccount:
|
||||
rbac:
|
||||
# Specifies whether RBAC resources should be created
|
||||
create: true
|
||||
additionalPermissions: {}
|
||||
additionalPermissions: []
|
||||
|
||||
# Annotations to add to the Deployment
|
||||
deploymentAnnotations: {}
|
||||
@ -34,6 +39,8 @@ podLabels: {}
|
||||
# Annotations to add to the Pod
|
||||
podAnnotations: {}
|
||||
|
||||
shareProcessNamespace: false
|
||||
|
||||
podSecurityContext:
|
||||
fsGroup: 65534
|
||||
|
||||
@ -44,15 +51,59 @@ securityContext:
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
|
||||
# Defaults to `ClusterFirst`.
|
||||
# Valid values are: `ClusterFirstWithHostNet`, `ClusterFirst`, `Default` or `None`.
|
||||
dnsPolicy:
|
||||
|
||||
priorityClassName: ""
|
||||
|
||||
terminationGracePeriodSeconds:
|
||||
|
||||
serviceMonitor:
|
||||
enabled: false
|
||||
# force namespace
|
||||
# namespace: monitoring
|
||||
|
||||
# Fallback to the prometheus default unless specified
|
||||
# interval: 10s
|
||||
|
||||
## scheme: HTTP scheme to use for scraping. Can be used with `tlsConfig` for example if using istio mTLS.
|
||||
# scheme: ""
|
||||
|
||||
## tlsConfig: TLS configuration to use when scraping the endpoint. For example if using istio mTLS.
|
||||
## Of type: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#tlsconfig
|
||||
# tlsConfig: {}
|
||||
|
||||
# bearerTokenFile:
|
||||
# Fallback to the prometheus default unless specified
|
||||
# scrapeTimeout: 30s
|
||||
|
||||
## Used to pass Labels that are used by the Prometheus installed in your cluster to select Service Monitors to work with
|
||||
## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#prometheusspec
|
||||
additionalLabels: {}
|
||||
interval: 1m
|
||||
scrapeTimeout: 10s
|
||||
|
||||
## Used to pass annotations that are used by the Prometheus installed in your cluster to select Service Monitors to work with
|
||||
## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#prometheusspec
|
||||
annotations: {}
|
||||
|
||||
## Metric relabel configs to apply to samples before ingestion.
|
||||
## [Metric Relabeling](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs)
|
||||
metricRelabelings: []
|
||||
# - action: keep
|
||||
# regex: 'kube_(daemonset|deployment|pod|namespace|node|statefulset).+'
|
||||
# sourceLabels: [__name__]
|
||||
|
||||
## Relabel configs to apply to samples before ingestion.
|
||||
## [Relabeling](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config)
|
||||
relabelings: []
|
||||
# - sourceLabels: [__meta_kubernetes_pod_node_name]
|
||||
# separator: ;
|
||||
# regex: ^(.*)$
|
||||
# targetLabel: nodename
|
||||
# replacement: $1
|
||||
# action: replace
|
||||
|
||||
targetLabels: []
|
||||
|
||||
env: []
|
||||
|
||||
@ -100,6 +151,8 @@ logFormat: text
|
||||
interval: 1m
|
||||
triggerLoopOnEvent: false
|
||||
|
||||
namespaced: false
|
||||
|
||||
sources:
|
||||
- service
|
||||
- ingress
|
||||
@ -116,3 +169,16 @@ domainFilters: []
|
||||
provider: aws
|
||||
|
||||
extraArgs: []
|
||||
|
||||
secretConfiguration:
|
||||
enabled: false
|
||||
mountPath: ""
|
||||
subPath: ""
|
||||
data: {}
|
||||
# credentials: |
|
||||
# [default]
|
||||
# aws_access_key_id = $SECRET_ACCESS_KEY
|
||||
# aws_secret_access_key = $SECRET_ACCESS_KEY
|
||||
|
||||
deploymentStrategy:
|
||||
type: Recreate
|
||||
|
@ -2,15 +2,22 @@
|
||||
timeout: 5000s
|
||||
options:
|
||||
substitution_option: ALLOW_LOOSE
|
||||
machineType: 'N1_HIGHCPU_8'
|
||||
steps:
|
||||
- name: "gcr.io/k8s-staging-test-infra/gcb-docker-gcloud:v20211118-2f2d816b90"
|
||||
entrypoint: make
|
||||
- name: "gcr.io/k8s-staging-test-infra/gcb-docker-gcloud:v20230206-8160eea68e"
|
||||
entrypoint: bash
|
||||
env:
|
||||
- DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
- VERSION=$_GIT_TAG
|
||||
- PULL_BASE_REF=$_PULL_BASE_REF
|
||||
- HOME=/root
|
||||
args:
|
||||
- release.staging
|
||||
- -c
|
||||
- |
|
||||
gcloud auth configure-docker
|
||||
/buildx-entrypoint version
|
||||
apk add musl-dev gcc
|
||||
make release.staging
|
||||
substitutions:
|
||||
# _GIT_TAG will be filled with a git-based tag for the image, of the form vYYYYMMDD-hash, and
|
||||
# can be used as a substitution
|
||||
|
@ -102,6 +102,14 @@ var (
|
||||
Help: "Number of Registry A records.",
|
||||
},
|
||||
)
|
||||
registryAAAARecords = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "external_dns",
|
||||
Subsystem: "registry",
|
||||
Name: "aaaa_records",
|
||||
Help: "Number of Registry AAAA records.",
|
||||
},
|
||||
)
|
||||
sourceARecords = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "external_dns",
|
||||
@ -110,6 +118,14 @@ var (
|
||||
Help: "Number of Source A records.",
|
||||
},
|
||||
)
|
||||
sourceAAAARecords = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "external_dns",
|
||||
Subsystem: "source",
|
||||
Name: "aaaa_records",
|
||||
Help: "Number of Source AAAA records.",
|
||||
},
|
||||
)
|
||||
verifiedARecords = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "external_dns",
|
||||
@ -118,6 +134,14 @@ var (
|
||||
Help: "Number of DNS A-records that exists both in source and registry.",
|
||||
},
|
||||
)
|
||||
verifiedAAAARecords = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "external_dns",
|
||||
Subsystem: "controller",
|
||||
Name: "verified_aaaa_records",
|
||||
Help: "Number of DNS AAAA-records that exists both in source and registry.",
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -130,8 +154,11 @@ func init() {
|
||||
prometheus.MustRegister(deprecatedSourceErrors)
|
||||
prometheus.MustRegister(controllerNoChangesTotal)
|
||||
prometheus.MustRegister(registryARecords)
|
||||
prometheus.MustRegister(registryAAAARecords)
|
||||
prometheus.MustRegister(sourceARecords)
|
||||
prometheus.MustRegister(sourceAAAARecords)
|
||||
prometheus.MustRegister(verifiedARecords)
|
||||
prometheus.MustRegister(verifiedAAAARecords)
|
||||
}
|
||||
|
||||
// Controller is responsible for orchestrating the different components.
|
||||
@ -167,9 +194,13 @@ func (c *Controller) RunOnce(ctx context.Context) error {
|
||||
deprecatedRegistryErrors.Inc()
|
||||
return err
|
||||
}
|
||||
|
||||
missingRecords := c.Registry.MissingRecords()
|
||||
|
||||
registryEndpointsTotal.Set(float64(len(records)))
|
||||
regARecords := filterARecords(records)
|
||||
registryARecords.Set(float64(len(regARecords)))
|
||||
regARecords, regAAAARecords := countAddressRecords(records)
|
||||
registryARecords.Set(float64(regARecords))
|
||||
registryAAAARecords.Set(float64(regAAAARecords))
|
||||
ctx = context.WithValue(ctx, provider.RecordsContextKey, records)
|
||||
|
||||
endpoints, err := c.Source.Endpoints(ctx)
|
||||
@ -179,12 +210,37 @@ func (c *Controller) RunOnce(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
sourceEndpointsTotal.Set(float64(len(endpoints)))
|
||||
srcARecords := filterARecords(endpoints)
|
||||
sourceARecords.Set(float64(len(srcARecords)))
|
||||
vRecords := fetchMatchingARecords(endpoints, records)
|
||||
verifiedARecords.Set(float64(len(vRecords)))
|
||||
srcARecords, srcAAAARecords := countAddressRecords(endpoints)
|
||||
sourceARecords.Set(float64(srcARecords))
|
||||
sourceAAAARecords.Set(float64(srcAAAARecords))
|
||||
vARecords, vAAAARecords := countMatchingAddressRecords(endpoints, records)
|
||||
verifiedARecords.Set(float64(vARecords))
|
||||
verifiedAAAARecords.Set(float64(vAAAARecords))
|
||||
endpoints = c.Registry.AdjustEndpoints(endpoints)
|
||||
|
||||
if len(missingRecords) > 0 {
|
||||
// Add missing records before the actual plan is applied.
|
||||
// This prevents the problems when the missing TXT record needs to be
|
||||
// created and deleted/upserted in the same batch.
|
||||
missingRecordsPlan := &plan.Plan{
|
||||
Policies: []plan.Policy{c.Policy},
|
||||
Missing: missingRecords,
|
||||
DomainFilter: endpoint.MatchAllDomainFilters{c.DomainFilter, c.Registry.GetDomainFilter()},
|
||||
PropertyComparator: c.Registry.PropertyValuesEqual,
|
||||
ManagedRecords: c.ManagedRecordTypes,
|
||||
}
|
||||
missingRecordsPlan = missingRecordsPlan.Calculate()
|
||||
if missingRecordsPlan.Changes.HasChanges() {
|
||||
err = c.Registry.ApplyChanges(ctx, missingRecordsPlan.Changes)
|
||||
if err != nil {
|
||||
registryErrorsTotal.Inc()
|
||||
deprecatedRegistryErrors.Inc()
|
||||
return err
|
||||
}
|
||||
log.Info("All missing records are created")
|
||||
}
|
||||
}
|
||||
|
||||
plan := &plan.Plan{
|
||||
Policies: []plan.Policy{c.Policy},
|
||||
Current: records,
|
||||
@ -212,37 +268,55 @@ func (c *Controller) RunOnce(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Checks and returns the intersection of A records in endpoint and registry.
|
||||
func fetchMatchingARecords(endpoints []*endpoint.Endpoint, registryRecords []*endpoint.Endpoint) []string {
|
||||
aRecords := filterARecords(endpoints)
|
||||
recordsMap := make(map[string]struct{})
|
||||
// Counts the intersections of A and AAAA records in endpoint and registry.
|
||||
func countMatchingAddressRecords(endpoints []*endpoint.Endpoint, registryRecords []*endpoint.Endpoint) (int, int) {
|
||||
recordsMap := make(map[string]map[string]struct{})
|
||||
for _, regRecord := range registryRecords {
|
||||
recordsMap[regRecord.DNSName] = struct{}{}
|
||||
if _, found := recordsMap[regRecord.DNSName]; !found {
|
||||
recordsMap[regRecord.DNSName] = make(map[string]struct{})
|
||||
}
|
||||
recordsMap[regRecord.DNSName][regRecord.RecordType] = struct{}{}
|
||||
}
|
||||
var cm []string
|
||||
for _, sourceRecord := range aRecords {
|
||||
if _, found := recordsMap[sourceRecord]; found {
|
||||
cm = append(cm, sourceRecord)
|
||||
aCount := 0
|
||||
aaaaCount := 0
|
||||
for _, sourceRecord := range endpoints {
|
||||
if _, found := recordsMap[sourceRecord.DNSName]; found {
|
||||
if _, found := recordsMap[sourceRecord.DNSName][sourceRecord.RecordType]; found {
|
||||
switch sourceRecord.RecordType {
|
||||
case endpoint.RecordTypeA:
|
||||
aCount++
|
||||
case endpoint.RecordTypeAAAA:
|
||||
aaaaCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cm
|
||||
return aCount, aaaaCount
|
||||
}
|
||||
|
||||
func filterARecords(endpoints []*endpoint.Endpoint) []string {
|
||||
var aRecords []string
|
||||
func countAddressRecords(endpoints []*endpoint.Endpoint) (int, int) {
|
||||
aCount := 0
|
||||
aaaaCount := 0
|
||||
for _, endPoint := range endpoints {
|
||||
if endPoint.RecordType == endpoint.RecordTypeA {
|
||||
aRecords = append(aRecords, endPoint.DNSName)
|
||||
switch endPoint.RecordType {
|
||||
case endpoint.RecordTypeA:
|
||||
aCount++
|
||||
case endpoint.RecordTypeAAAA:
|
||||
aaaaCount++
|
||||
}
|
||||
}
|
||||
return aRecords
|
||||
return aCount, aaaaCount
|
||||
}
|
||||
|
||||
// ScheduleRunOnce makes sure execution happens at most once per interval.
|
||||
func (c *Controller) ScheduleRunOnce(now time.Time) {
|
||||
c.nextRunAtMux.Lock()
|
||||
defer c.nextRunAtMux.Unlock()
|
||||
c.nextRunAt = now.Add(c.MinEventSyncInterval)
|
||||
// schedule only if a reconciliation is not already planned
|
||||
// to happen in the following c.MinEventSyncInterval
|
||||
if !c.nextRunAt.Before(now.Add(c.MinEventSyncInterval)) {
|
||||
c.nextRunAt = now.Add(c.MinEventSyncInterval)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Controller) ShouldRunOnce(now time.Time) bool {
|
||||
|
@ -19,12 +19,14 @@ package controller
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"math"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/internal/testutils"
|
||||
"sigs.k8s.io/external-dns/pkg/apis/externaldns"
|
||||
@ -82,32 +84,20 @@ func (p *errorMockProvider) Records(ctx context.Context) ([]*endpoint.Endpoint,
|
||||
|
||||
// ApplyChanges validates that the passed in changes satisfy the assumptions.
|
||||
func (p *mockProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
||||
if len(changes.Create) != len(p.ExpectChanges.Create) {
|
||||
return errors.New("number of created records is wrong")
|
||||
if err := verifyEndpoints(changes.Create, p.ExpectChanges.Create); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range changes.Create {
|
||||
if changes.Create[i].DNSName != p.ExpectChanges.Create[i].DNSName || !changes.Create[i].Targets.Same(p.ExpectChanges.Create[i].Targets) {
|
||||
return errors.New("created record is wrong")
|
||||
}
|
||||
if err := verifyEndpoints(changes.UpdateNew, p.ExpectChanges.UpdateNew); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range changes.UpdateNew {
|
||||
if changes.UpdateNew[i].DNSName != p.ExpectChanges.UpdateNew[i].DNSName || !changes.UpdateNew[i].Targets.Same(p.ExpectChanges.UpdateNew[i].Targets) {
|
||||
return errors.New("delete record is wrong")
|
||||
}
|
||||
if err := verifyEndpoints(changes.UpdateOld, p.ExpectChanges.UpdateOld); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range changes.UpdateOld {
|
||||
if changes.UpdateOld[i].DNSName != p.ExpectChanges.UpdateOld[i].DNSName || !changes.UpdateOld[i].Targets.Same(p.ExpectChanges.UpdateOld[i].Targets) {
|
||||
return errors.New("delete record is wrong")
|
||||
}
|
||||
}
|
||||
|
||||
for i := range changes.Delete {
|
||||
if changes.Delete[i].DNSName != p.ExpectChanges.Delete[i].DNSName || !changes.Delete[i].Targets.Same(p.ExpectChanges.Delete[i].Targets) {
|
||||
return errors.New("delete record is wrong")
|
||||
}
|
||||
if err := verifyEndpoints(changes.Delete, p.ExpectChanges.Delete); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(ctx.Value(provider.RecordsContextKey), p.RecordsStore) {
|
||||
@ -116,6 +106,21 @@ func (p *mockProvider) ApplyChanges(ctx context.Context, changes *plan.Changes)
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyEndpoints(actual, expected []*endpoint.Endpoint) error {
|
||||
if len(actual) != len(expected) {
|
||||
return errors.New("number of records is wrong")
|
||||
}
|
||||
sort.Slice(actual, func(i, j int) bool {
|
||||
return actual[i].DNSName < actual[j].DNSName
|
||||
})
|
||||
for i := range actual {
|
||||
if actual[i].DNSName != expected[i].DNSName || !actual[i].Targets.Same(expected[i].Targets) {
|
||||
return errors.New("record is wrong")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// newMockProvider creates a new mockProvider returning the given endpoints and validating the desired changes.
|
||||
func newMockProvider(endpoints []*endpoint.Endpoint, changes *plan.Changes) provider.Provider {
|
||||
dnsProvider := &mockProvider{
|
||||
@ -131,7 +136,7 @@ func TestRunOnce(t *testing.T) {
|
||||
// Fake some desired endpoints coming from our source.
|
||||
source := new(testutils.MockSource)
|
||||
cfg := externaldns.NewConfig()
|
||||
cfg.ManagedDNSRecordTypes = []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}
|
||||
cfg.ManagedDNSRecordTypes = []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}
|
||||
source.On("Endpoints").Return([]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "create-record",
|
||||
@ -143,6 +148,16 @@ func TestRunOnce(t *testing.T) {
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"8.8.4.4"},
|
||||
},
|
||||
{
|
||||
DNSName: "create-aaaa-record",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::1"},
|
||||
},
|
||||
{
|
||||
DNSName: "update-aaaa-record",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::2"},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
// Fake some existing records in our DNS provider and validate some desired changes.
|
||||
@ -158,18 +173,32 @@ func TestRunOnce(t *testing.T) {
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"4.3.2.1"},
|
||||
},
|
||||
{
|
||||
DNSName: "update-aaaa-record",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::3"},
|
||||
},
|
||||
{
|
||||
DNSName: "delete-aaaa-record",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::4"},
|
||||
},
|
||||
},
|
||||
&plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
{DNSName: "create-aaaa-record", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1"}},
|
||||
{DNSName: "create-record", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||
},
|
||||
UpdateNew: []*endpoint.Endpoint{
|
||||
{DNSName: "update-aaaa-record", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::2"}},
|
||||
{DNSName: "update-record", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"8.8.4.4"}},
|
||||
},
|
||||
UpdateOld: []*endpoint.Endpoint{
|
||||
{DNSName: "update-aaaa-record", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::3"}},
|
||||
{DNSName: "update-record", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"8.8.8.8"}},
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
{DNSName: "delete-aaaa-record", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::4"}},
|
||||
{DNSName: "delete-record", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"4.3.2.1"}},
|
||||
},
|
||||
},
|
||||
@ -192,6 +221,7 @@ func TestRunOnce(t *testing.T) {
|
||||
source.AssertExpectations(t)
|
||||
// check the verified records
|
||||
assert.Equal(t, math.Float64bits(1), valueFromMetric(verifiedARecords))
|
||||
assert.Equal(t, math.Float64bits(1), valueFromMetric(verifiedAAAARecords))
|
||||
}
|
||||
|
||||
func valueFromMetric(metric prometheus.Gauge) uint64 {
|
||||
@ -236,12 +266,23 @@ func TestShouldRunOnce(t *testing.T) {
|
||||
|
||||
// But not two times
|
||||
assert.False(t, ctrl.ShouldRunOnce(now))
|
||||
|
||||
// Multiple ingresses or services changes, closer than MinInterval from each other
|
||||
firstChangeTime := now
|
||||
secondChangeTime := firstChangeTime.Add(time.Second)
|
||||
// First change
|
||||
ctrl.ScheduleRunOnce(firstChangeTime)
|
||||
// Second change
|
||||
ctrl.ScheduleRunOnce(secondChangeTime)
|
||||
// Should not postpone the reconciliation further than firstChangeTime + MinInterval
|
||||
now = now.Add(ctrl.MinEventSyncInterval)
|
||||
assert.True(t, ctrl.ShouldRunOnce(now))
|
||||
}
|
||||
|
||||
func testControllerFiltersDomains(t *testing.T, configuredEndpoints []*endpoint.Endpoint, domainFilter endpoint.DomainFilterInterface, providerEndpoints []*endpoint.Endpoint, expectedChanges []*plan.Changes) {
|
||||
t.Helper()
|
||||
cfg := externaldns.NewConfig()
|
||||
cfg.ManagedDNSRecordTypes = []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}
|
||||
cfg.ManagedDNSRecordTypes = []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}
|
||||
|
||||
source := new(testutils.MockSource)
|
||||
source.On("Endpoints").Return(configuredEndpoints, nil)
|
||||
@ -270,6 +311,51 @@ func testControllerFiltersDomains(t *testing.T, configuredEndpoints []*endpoint.
|
||||
}
|
||||
}
|
||||
|
||||
type noopRegistryWithMissing struct {
|
||||
*registry.NoopRegistry
|
||||
missingRecords []*endpoint.Endpoint
|
||||
}
|
||||
|
||||
func (r *noopRegistryWithMissing) MissingRecords() []*endpoint.Endpoint {
|
||||
return r.missingRecords
|
||||
}
|
||||
|
||||
func testControllerFiltersDomainsWithMissing(t *testing.T, configuredEndpoints []*endpoint.Endpoint, domainFilter endpoint.DomainFilterInterface, providerEndpoints, missingEndpoints []*endpoint.Endpoint, expectedChanges []*plan.Changes) {
|
||||
t.Helper()
|
||||
cfg := externaldns.NewConfig()
|
||||
cfg.ManagedDNSRecordTypes = []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}
|
||||
|
||||
source := new(testutils.MockSource)
|
||||
source.On("Endpoints").Return(configuredEndpoints, nil)
|
||||
|
||||
// Fake some existing records in our DNS provider and validate some desired changes.
|
||||
provider := &filteredMockProvider{
|
||||
RecordsStore: providerEndpoints,
|
||||
}
|
||||
noop, err := registry.NewNoopRegistry(provider)
|
||||
require.NoError(t, err)
|
||||
|
||||
r := &noopRegistryWithMissing{
|
||||
NoopRegistry: noop,
|
||||
missingRecords: missingEndpoints,
|
||||
}
|
||||
|
||||
ctrl := &Controller{
|
||||
Source: source,
|
||||
Registry: r,
|
||||
Policy: &plan.SyncPolicy{},
|
||||
DomainFilter: domainFilter,
|
||||
ManagedRecordTypes: cfg.ManagedDNSRecordTypes,
|
||||
}
|
||||
|
||||
assert.NoError(t, ctrl.RunOnce(context.Background()))
|
||||
assert.Equal(t, 1, provider.RecordsCallCount)
|
||||
require.Len(t, provider.ApplyChangesCalls, len(expectedChanges))
|
||||
for i, change := range expectedChanges {
|
||||
assert.Equal(t, *change, *provider.ApplyChangesCalls[i])
|
||||
}
|
||||
}
|
||||
|
||||
func TestControllerSkipsEmptyChanges(t *testing.T) {
|
||||
testControllerFiltersDomains(
|
||||
t,
|
||||
@ -469,6 +555,85 @@ func TestVerifyARecords(t *testing.T) {
|
||||
}},
|
||||
)
|
||||
assert.Equal(t, math.Float64bits(2), valueFromMetric(verifiedARecords))
|
||||
assert.Equal(t, math.Float64bits(0), valueFromMetric(verifiedAAAARecords))
|
||||
}
|
||||
|
||||
func TestVerifyAAAARecords(t *testing.T) {
|
||||
testControllerFiltersDomains(
|
||||
t,
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "create-record.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::1"},
|
||||
},
|
||||
{
|
||||
DNSName: "some-record.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::2"},
|
||||
},
|
||||
},
|
||||
endpoint.NewDomainFilter([]string{"used.tld"}),
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "some-record.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::2"},
|
||||
},
|
||||
{
|
||||
DNSName: "create-record.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::1"},
|
||||
},
|
||||
},
|
||||
[]*plan.Changes{},
|
||||
)
|
||||
assert.Equal(t, math.Float64bits(2), valueFromMetric(verifiedAAAARecords))
|
||||
|
||||
testControllerFiltersDomains(
|
||||
t,
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "some-record.1.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::1"},
|
||||
},
|
||||
{
|
||||
DNSName: "some-record.2.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::2"},
|
||||
},
|
||||
{
|
||||
DNSName: "some-record.3.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::3"},
|
||||
},
|
||||
},
|
||||
endpoint.NewDomainFilter([]string{"used.tld"}),
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "some-record.1.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::1"},
|
||||
},
|
||||
{
|
||||
DNSName: "some-record.2.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::2"},
|
||||
},
|
||||
},
|
||||
[]*plan.Changes{{
|
||||
Create: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "some-record.3.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::3"},
|
||||
},
|
||||
},
|
||||
}},
|
||||
)
|
||||
assert.Equal(t, math.Float64bits(0), valueFromMetric(verifiedARecords))
|
||||
assert.Equal(t, math.Float64bits(2), valueFromMetric(verifiedAAAARecords))
|
||||
}
|
||||
|
||||
func TestARecords(t *testing.T) {
|
||||
@ -517,3 +682,104 @@ func TestARecords(t *testing.T) {
|
||||
assert.Equal(t, math.Float64bits(2), valueFromMetric(sourceARecords))
|
||||
assert.Equal(t, math.Float64bits(1), valueFromMetric(registryARecords))
|
||||
}
|
||||
|
||||
// TestMissingRecordsApply validates that the missing records result in the dedicated plan apply.
|
||||
func TestMissingRecordsApply(t *testing.T) {
|
||||
testControllerFiltersDomainsWithMissing(
|
||||
t,
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "record1.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"1.2.3.4"},
|
||||
},
|
||||
{
|
||||
DNSName: "record2.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"8.8.8.8"},
|
||||
},
|
||||
},
|
||||
endpoint.NewDomainFilter([]string{"used.tld"}),
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "record1.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"1.2.3.4"},
|
||||
},
|
||||
},
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "a-record1.used.tld",
|
||||
RecordType: endpoint.RecordTypeTXT,
|
||||
Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=owner\""},
|
||||
},
|
||||
},
|
||||
[]*plan.Changes{
|
||||
// Missing record had its own plan applied.
|
||||
{
|
||||
Create: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "a-record1.used.tld",
|
||||
RecordType: endpoint.RecordTypeTXT,
|
||||
Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=owner\""},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Create: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "record2.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"8.8.8.8"},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAAAARecords(t *testing.T) {
|
||||
testControllerFiltersDomains(
|
||||
t,
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "record1.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::1"},
|
||||
},
|
||||
{
|
||||
DNSName: "record2.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::2"},
|
||||
},
|
||||
{
|
||||
DNSName: "_mysql-svc._tcp.mysql.used.tld",
|
||||
RecordType: endpoint.RecordTypeSRV,
|
||||
Targets: endpoint.Targets{"0 50 30007 mysql.used.tld"},
|
||||
},
|
||||
},
|
||||
endpoint.NewDomainFilter([]string{"used.tld"}),
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "record1.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::1"},
|
||||
},
|
||||
{
|
||||
DNSName: "_mysql-svc._tcp.mysql.used.tld",
|
||||
RecordType: endpoint.RecordTypeSRV,
|
||||
Targets: endpoint.Targets{"0 50 30007 mysql.used.tld"},
|
||||
},
|
||||
},
|
||||
[]*plan.Changes{{
|
||||
Create: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "record2.used.tld",
|
||||
RecordType: endpoint.RecordTypeAAAA,
|
||||
Targets: endpoint.Targets{"2001:DB8::2"},
|
||||
},
|
||||
},
|
||||
}},
|
||||
)
|
||||
assert.Equal(t, math.Float64bits(2), valueFromMetric(sourceAAAARecords))
|
||||
assert.Equal(t, math.Float64bits(1), valueFromMetric(registryAAAARecords))
|
||||
}
|
||||
|
@ -3,3 +3,5 @@
|
||||
## Chart Changes
|
||||
|
||||
When contributing chart changes please follow the same process as when contributing other content but also please **DON'T** modify _Chart.yaml_ in the PR as this would result in a chart release when merged and will mean that your PR will need modifying before it can be accepted. The chart version will be updated as part of the PR to release the chart.
|
||||
|
||||
Please **DO** add your changes to the _CHANGELOG.md_ file in the chart directory under the `## [UNRELEASED]` section, if there isn't an uncommented `## [UNRELEASED]` section please copy the commented out template and use that.
|
||||
|
18
docs/contributing/crd-source/dnsendpoint-aws-example.yaml
Normal file
18
docs/contributing/crd-source/dnsendpoint-aws-example.yaml
Normal file
@ -0,0 +1,18 @@
|
||||
apiVersion: externaldns.k8s.io/v1alpha1
|
||||
kind: DNSEndpoint
|
||||
metadata:
|
||||
name: examplednsrecord
|
||||
spec:
|
||||
endpoints:
|
||||
- dnsName: subdomain.foo.bar.com
|
||||
providerSpecific:
|
||||
- name: "aws/failover"
|
||||
value: "PRIMARY"
|
||||
- name: "aws/health-check-id"
|
||||
value: "asdf1234-as12-as12-as12-asdf12345678"
|
||||
- name: "aws/evaluate-target-health"
|
||||
value: "true"
|
||||
recordType: CNAME
|
||||
setIdentifier: some-unique-id
|
||||
targets:
|
||||
- other-subdomain.foo.bar.com
|
@ -1,7 +1,7 @@
|
||||
# Quick Start
|
||||
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
- [Go 1.17+](https://golang.org/dl/)
|
||||
- [Go 1.19+](https://golang.org/dl/)
|
||||
- [Go modules](https://github.com/golang/go/wiki/Modules)
|
||||
- [golangci-lint](https://github.com/golangci/golangci-lint)
|
||||
- [Docker](https://docs.docker.com/install/)
|
||||
@ -31,7 +31,7 @@ make build.docker
|
||||
|
||||
ExternalDNS's sources of DNS records live in package [source](../../source). They implement the `Source` interface that has a single method `Endpoints` which returns the represented source's objects converted to `Endpoints`. Endpoints are just a tuple of DNS name and target where target can be an IP or another hostname.
|
||||
|
||||
For example, the `ServiceSource` returns all Services converted to `Endpoints` where the hostname is the value of the `external-dns.alpha.kubernetes.io/hostname` annotation and the target is the IP of the load balancer or where the hostname is the value of the `external-dns.alpha.kubernetes.io/internal-hostname` annotation and the target is the IP of the service CLusterIP.
|
||||
For example, the `ServiceSource` returns all Services converted to `Endpoints` where the hostname is the value of the `external-dns.alpha.kubernetes.io/hostname` annotation and the target is the IP of the load balancer or where the hostname is the value of the `external-dns.alpha.kubernetes.io/internal-hostname` annotation and the target is the IP of the service ClusterIP.
|
||||
|
||||
This list of endpoints is passed to the [Plan](../../plan) which determines the difference between the current DNS records and the desired list of `Endpoints`.
|
||||
|
||||
|
31
docs/faq.md
31
docs/faq.md
@ -34,7 +34,7 @@ As stated in the README, we are currently looking for stable maintainers for tho
|
||||
|
||||
### Which Kubernetes objects are supported?
|
||||
|
||||
Services exposed via `type=LoadBalancer`, `type=ExternalName` and for the hostnames defined in Ingress objects as well as headless hostPort services. An initial effort to support type `NodePort` was started as of May 2018 and it is in progress at the time of writing.
|
||||
Services exposed via `type=LoadBalancer`, `type=ExternalName`, `type=NodePort`, and for the hostnames defined in Ingress objects as well as [headless hostPort](tutorials/hostport.md) services.
|
||||
|
||||
### How do I specify a DNS name for my Kubernetes objects?
|
||||
|
||||
@ -88,7 +88,7 @@ ExternalDNS will allow you to opt into any Services and Ingresses that you want
|
||||
|
||||
### I'm afraid you will mess up my DNS records!
|
||||
|
||||
ExternalDNS since v0.3 implements the concept of owning DNS records. This means that ExternalDNS will keep track of which records it has control over, and will never modify any records over which it doesn't have control. This is a fundamental requirement to operate ExternalDNS safely when there might be other actors creating DNS records in the same target space.
|
||||
Since v0.3, ExternalDNS can be configured to use an ownership registry. When this option is enabled, ExternalDNS will keep track of which records it has control over, and will never modify any records over which it doesn't have control. This is a fundamental requirement to operate ExternalDNS safely when there might be other actors creating DNS records in the same target space.
|
||||
|
||||
For now ExternalDNS uses TXT records to label owned records, and there might be other alternatives coming in the future releases.
|
||||
|
||||
@ -178,16 +178,18 @@ You can use the host label in the metric to figure out if the request was agains
|
||||
|
||||
Here is the full list of available metrics provided by ExternalDNS:
|
||||
|
||||
| Name | Description | Type |
|
||||
| --------------------------------------------------- | ------------------------------------------------------- | ------- |
|
||||
| external_dns_controller_last_sync_timestamp_seconds | Timestamp of last successful sync with the DNS provider | Gauge |
|
||||
| external_dns_registry_endpoints_total | Number of Endpoints in all sources | Gauge |
|
||||
| external_dns_registry_errors_total | Number of Registry errors | Counter |
|
||||
| external_dns_source_endpoints_total | Number of Endpoints in the registry | Gauge |
|
||||
| external_dns_source_errors_total | Number of Source errors | Counter |
|
||||
| external_dns_controller_verified_records | Number of DNS A-records that exists both in | Gauge |
|
||||
| | source & registry | |
|
||||
| Name | Description | Type |
|
||||
| --------------------------------------------------- | ------------------------------------------------------------------ | ------- |
|
||||
| external_dns_controller_last_sync_timestamp_seconds | Timestamp of last successful sync with the DNS provider | Gauge |
|
||||
| external_dns_registry_endpoints_total | Number of Endpoints in all sources | Gauge |
|
||||
| external_dns_registry_errors_total | Number of Registry errors | Counter |
|
||||
| external_dns_source_endpoints_total | Number of Endpoints in the registry | Gauge |
|
||||
| external_dns_source_errors_total | Number of Source errors | Counter |
|
||||
| external_dns_controller_verified_aaaa_records | Number of DNS AAAA-records that exists both in source and registry | Gauge |
|
||||
| external_dns_controller_verified_a_records | Number of DNS A-records that exists both in source and registry | Gauge |
|
||||
| external_dns_registry_aaaa_records | Number of AAAA records in registry | Gauge |
|
||||
| external_dns_registry_a_records | Number of A records in registry | Gauge |
|
||||
| external_dns_source_aaaa_records | Number of AAAA records in source | Gauge |
|
||||
| external_dns_source_a_records | Number of A records in source | Gauge |
|
||||
|
||||
### How can I run ExternalDNS under a specific GCP Service Account, e.g. to access DNS records in other projects?
|
||||
@ -205,7 +207,7 @@ $ docker run \
|
||||
-e EXTERNAL_DNS_SOURCE=$'service\ningress' \
|
||||
-e EXTERNAL_DNS_PROVIDER=google \
|
||||
-e EXTERNAL_DNS_DOMAIN_FILTER=$'foo.com\nbar.com' \
|
||||
k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
time="2017-08-08T14:10:26Z" level=info msg="config: &{APIServerURL: KubeConfig: Sources:[service ingress] Namespace: ...
|
||||
```
|
||||
|
||||
@ -289,6 +291,9 @@ Conversely, to force the public IP: `external-dns.alpha.kubernetes.io/access=pub
|
||||
|
||||
If this annotation is not set, and the node has both public and private IP addresses, then the public IP will be used by default.
|
||||
|
||||
Some loadbalancer implementations assign multiple IP addresses as external addresses. You can filter the generated targets by their networks
|
||||
using `--target-net-filter=10.0.0.0/8` or `--exclude-target-net=10.0.0.0/8`.
|
||||
|
||||
### Can external-dns manage(add/remove) records in a hosted zone which is setup in different AWS account?
|
||||
|
||||
Yes, give it the correct cross-account/assume-role permissions and use the `--aws-assume-role` flag https://github.com/kubernetes-sigs/external-dns/pull/524#issue-181256561
|
||||
@ -303,7 +308,7 @@ Separate them by `,`.
|
||||
When we tag a new release, we push a container image to the Kubernetes projects official container registry with the following name:
|
||||
|
||||
```
|
||||
k8s.gcr.io/external-dns/external-dns
|
||||
registry.k8s.io/external-dns/external-dns
|
||||
```
|
||||
|
||||
As tags, you use the external-dns release of choice(i.e. `v0.7.6`). A `latest` tag is not provided in the container registry.
|
||||
|
Before Width: | Height: | Size: 251 KiB After Width: | Height: | Size: 251 KiB |
@ -32,8 +32,7 @@ The following presents two ways to implement the registry, and we are planning t
|
||||
|
||||
This implementation idea is borrowed from [Mate](https://github.com/linki/mate/)
|
||||
|
||||
Each record created by external-dns is accompanied by the TXT record, which internally stores the external-dns identifier. For example, if external dns with `owner-id="external-dns-1"` record to be created with dns name `foo.zone.org`, external-dns will create a TXT record with the same dns name `foo.zone.org` and injected value of `"external-dns-1"`. The transfer of ownership can be done by modifying the value of the TXT record. If no TXT record exists for the record or the value does not match its own `owner-id`, then external-dns will simply ignore it.
|
||||
|
||||
Each record created by external-dns is accompanied by the TXT record, which internally stores the external-dns identifier. For example, if external dns with `owner-id="external-dns-1"` record to be created with dns name `foo.zone.org`, external-dns will create a TXT record with the same dns name `<record_type>-foo.zone.org` and injected value of `"external-dns-1"`. The transfer of ownership can be done by modifying the value of the TXT record. If no TXT record exists for the record or the value does not match its own `owner-id`, then external-dns will simply ignore it.
|
||||
|
||||
#### Goods
|
||||
1. Easy to guarantee cross-cluster ownership safety
|
||||
|
15
docs/registry.md
Normal file
15
docs/registry.md
Normal file
@ -0,0 +1,15 @@
|
||||
### TXT Registry migration to a new format ###
|
||||
|
||||
In order to support more record types and be able to track ownership without TXT record name clash, a new TXT record is introduced.
|
||||
It contains record type it manages, e.g.:
|
||||
* A record foo.example.com will be tracked with classic foo.example.com TXT record
|
||||
* At the same time a new TXT record will be created a-foo.example.com
|
||||
|
||||
Prefix and suffix are extended with %{record_type} template where the user can control how prefixed/suffixed records should look like.
|
||||
|
||||
In order to maintain compatibility, both records will be maintained for some time, in order to have downgrade possibility.
|
||||
The controller will try to create the "new format" TXT records if they are not present to ease the migration from the versions < 0.12.0.
|
||||
|
||||
Later on, the old format will be dropped and only the new format will be kept (<record_type>-<endpoint_name>).
|
||||
|
||||
Cleanup will be done by controller itself.
|
@ -28,9 +28,10 @@ You must be an official maintainer of the project to be able to do a release.
|
||||
- The step above will trigger the Kubernetes based CI/CD system [Prow](https://prow.k8s.io/?repo=kubernetes-sigs%2Fexternal-dns). Verify that a new image was built and uploaded to `gcr.io/k8s-staging-external-dns/external-dns`.
|
||||
- Create a PR in the [k8s.io repo](https://github.com/kubernetes/k8s.io) (see https://github.com/kubernetes/k8s.io/pull/540 for reference) by taking the current staging image using the sha256 digest. Once the PR is merged, the image will be live with the corresponding tag specified in the PR.
|
||||
- Verify that the image is pullable with the given tag (i.e. `v0.7.5`).
|
||||
- Branch out from the default branch and run `scripts/kustomize-version-udapter.sh` to update the image tag used in the kustomization.yaml.
|
||||
- Branch out from the default branch and run `scripts/kustomize-version-updater.sh` to update the image tag used in the kustomization.yaml.
|
||||
- Create an issue to release the corresponding Helm chart via the chart release process (below) assigned to a chart maintainer
|
||||
- Create a PR with the kustomize change.
|
||||
- Create a PR to replace all versions for docker images in the tutorials. A possible script to use is `sd registry.k8s.io/external-dns/external-dns:.* registry.k8s.io/external-dns/external-dns:v0.13.2 $(fd --type file)` which uses the `fd` and `sd` utilities.
|
||||
- Once the PR is merged, all is done :-)
|
||||
|
||||
## How to release a new chart version
|
||||
|
9
docs/scripts/copy_docs.sh
Executable file
9
docs/scripts/copy_docs.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
cp CONTRIBUTING.md code-of-conduct.md ./docs/
|
||||
|
||||
cp LICENSE ./docs/LICENSE.md
|
||||
|
||||
cp README.md ./docs/index.md
|
47
docs/scripts/docs.go
Normal file
47
docs/scripts/docs.go
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func removeLinkPrefixInIndex() {
|
||||
content, err := os.ReadFile("./docs/index.md")
|
||||
if err != nil {
|
||||
log.Fatalf("Could not read index.md file. Make sure to run copy_docs.sh script first. Original error: %s", err)
|
||||
}
|
||||
|
||||
updatedContent := strings.ReplaceAll(string(content), "](./docs/", "](")
|
||||
updatedContent = strings.ReplaceAll(updatedContent, "](docs/", "](")
|
||||
|
||||
f, err := os.OpenFile("./docs/index.md", os.O_RDWR, 0o644)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not open index.md file to update content. Original error: %s", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := f.WriteString(updatedContent); err != nil {
|
||||
log.Fatalf("Failed writing links update to index.md. Original error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
removeLinkPrefixInIndex()
|
||||
}
|
3
docs/scripts/index.html.gotmpl
Normal file
3
docs/scripts/index.html.gotmpl
Normal file
@ -0,0 +1,3 @@
|
||||
<head>
|
||||
<meta http-equiv="Refresh" content="0; url='/external-dns/{{.}}/'" />
|
||||
</head>
|
5
docs/scripts/requirements.txt
Normal file
5
docs/scripts/requirements.txt
Normal file
@ -0,0 +1,5 @@
|
||||
mkdocs-git-revision-date-localized-plugin == 1.0.0
|
||||
mkdocs == 1.3.0
|
||||
mkdocs-material == 8.2.8
|
||||
mkdocs-literate-nav == 0.4.0
|
||||
mike == 1.1.2
|
13
docs/ttl.md
13
docs/ttl.md
@ -36,8 +36,8 @@ Providers
|
||||
=========
|
||||
|
||||
- [x] AWS (Route53)
|
||||
- [ ] Azure
|
||||
- [ ] Cloudflare
|
||||
- [x] Azure
|
||||
- [x] Cloudflare
|
||||
- [x] DigitalOcean
|
||||
- [x] DNSimple
|
||||
- [x] Google
|
||||
@ -58,6 +58,13 @@ When the `external-dns.alpha.kubernetes.io/ttl` annotation is not provided, the
|
||||
The AWS Provider overrides the value to 300s when the TTL is 0.
|
||||
This value is a constant in the provider code.
|
||||
|
||||
## Azure
|
||||
TTL value should be between 1 and 2,147,483,647 seconds.
|
||||
By default it will be 300s.
|
||||
|
||||
## CloudFlare Provider
|
||||
CloudFlare overrides the value to "auto" when the TTL is 0.
|
||||
|
||||
### DigitalOcean Provider
|
||||
The DigitalOcean Provider overrides the value to 300s when the TTL is 0.
|
||||
This value is a constant in the provider code.
|
||||
@ -82,5 +89,5 @@ The TransIP Provider minimal TTL is used when the TTL is 0. The minimal TTL is 6
|
||||
### Vultr Provider
|
||||
The Vultr provider minimal TTL is used when the TTL is 0. The default is 1 hour.
|
||||
|
||||
### UltraDNS
|
||||
### UltraDNS
|
||||
The UltraDNS provider minimal TTL is used when the TTL is not provided. The default TTL is account level default TTL, if defined, otherwise 24 hours.
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Setting up ExternalDNS for Services on UKFast's SafeDNS
|
||||
# Setting up ExternalDNS for Services on ANS Group's SafeDNS
|
||||
|
||||
This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using SafeDNS.
|
||||
|
||||
@ -9,14 +9,14 @@ Make sure to use **>=0.11.0** version of ExternalDNS for this tutorial.
|
||||
If you want to learn about how to use the SafeDNS service read the following tutorials:
|
||||
To learn more about the use of SafeDNS in general, see the following page:
|
||||
|
||||
[UKFast's SafeDNS documentation](https://docs.ukfast.co.uk/domains/safedns/index.html).
|
||||
[ANS Group's SafeDNS documentation](https://docs.ukfast.co.uk/domains/safedns/index.html).
|
||||
|
||||
## Creating SafeDNS credentials
|
||||
|
||||
Generate a fresh API token for use with ExternalDNS, following the instructions
|
||||
at the UKFast developer [Getting-Started](https://developers.ukfast.io/getting-started)
|
||||
at the ANS Group developer [Getting-Started](https://developers.ukfast.io/getting-started)
|
||||
page. You will need to grant read/write access to the SafeDNS API. No access to
|
||||
any other UKFast service is required.
|
||||
any other ANS Group service is required.
|
||||
|
||||
The environment variable `SAFEDNS_TOKEN` must have a value of this token to run
|
||||
ExternalDNS with SafeDNS integration.
|
||||
@ -48,7 +48,7 @@ spec:
|
||||
- name: external-dns
|
||||
# You will need to check what the latest version is yourself:
|
||||
# https://github.com/kubernetes-sigs/external-dns/releases
|
||||
image: k8s.gcr.io/external-dns/external-dns:vX.Y.Z
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
# (optional) limit to only example.com domains; change to match the
|
||||
@ -114,7 +114,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.11.0
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
# (optional) limit to only example.com domains; change to match the
|
@ -57,7 +57,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.8.0
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # or ingress or both
|
||||
- --provider=akamai
|
||||
@ -143,7 +143,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.8.0
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # or ingress or both
|
||||
- --provider=akamai
|
||||
|
@ -113,7 +113,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -187,7 +187,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -241,8 +241,11 @@ spec:
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: foo
|
||||
servicePort: 80
|
||||
service:
|
||||
name: foo
|
||||
port:
|
||||
number: 80
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
## Verify ExternalDNS works (Service example)
|
||||
|
@ -1,10 +1,10 @@
|
||||
# Using ExternalDNS with alb-ingress-controller
|
||||
# Using ExternalDNS with aws-load-balancer-controller
|
||||
|
||||
This tutorial describes how to use ExternalDNS with the [aws-alb-ingress-controller][1].
|
||||
This tutorial describes how to use ExternalDNS with the [aws-load-balancer-controller][1].
|
||||
|
||||
[1]: https://kubernetes-sigs.github.io/aws-load-balancer-controller
|
||||
|
||||
## Setting up ExternalDNS and aws-alb-ingress-controller
|
||||
## Setting up ExternalDNS and aws-load-balancer-controller
|
||||
|
||||
Follow the [AWS tutorial](aws.md) to setup ExternalDNS for use in Kubernetes clusters
|
||||
running in AWS. Specify the `source=ingress` argument so that ExternalDNS will look
|
||||
@ -12,11 +12,11 @@ for hostnames in Ingress objects. In addition, you may wish to limit which Ingre
|
||||
objects are used as an ExternalDNS source via the `ingress-class` argument, but
|
||||
this is not required.
|
||||
|
||||
For help setting up the ALB Ingress Controller, follow the [Setup Guide][2].
|
||||
For help setting up the AWS Load Balancer Controller, follow the [Setup Guide][2].
|
||||
|
||||
[2]: https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/deploy/installation/
|
||||
|
||||
Note that the ALB ingress controller uses the same tags for [subnet auto-discovery][3]
|
||||
Note that the AWS Load Balancer Controller uses the same tags for [subnet auto-discovery][3]
|
||||
as Kubernetes does with the AWS cloud provider.
|
||||
|
||||
[3]: https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/deploy/subnet_discovery/
|
||||
@ -143,7 +143,7 @@ multiple aliases for the resulting ALB.
|
||||
## Dualstack ALBs
|
||||
|
||||
AWS [supports][4] both IPv4 and "dualstack" (both IPv4 and IPv6) interfaces for ALBs.
|
||||
The ALB ingress controller uses the `alb.ingress.kubernetes.io/ip-address-type`
|
||||
The AWS Load Balancer Controller uses the `alb.ingress.kubernetes.io/ip-address-type`
|
||||
annotation (which defaults to `ipv4`) to determine this. If this annotation is
|
||||
set to `dualstack` then ExternalDNS will create two alias records (one A record
|
||||
and one AAAA record) for each hostname associated with the Ingress object.
|
@ -81,7 +81,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
env:
|
||||
- name: AWS_REGION
|
||||
value: us-east-1 # put your CloudMap NameSpace region
|
||||
@ -148,7 +148,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
env:
|
||||
- name: AWS_REGION
|
||||
value: us-east-1 # put your CloudMap NameSpace region
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,60 +3,20 @@
|
||||
This tutorial describes how to set up ExternalDNS for managing records in Azure Private DNS.
|
||||
|
||||
It comprises of the following steps:
|
||||
1) Install NGINX Ingress Controller
|
||||
2) Provision Azure Private DNS
|
||||
3) Configure service principal for managing the zone
|
||||
4) Deploy ExternalDNS
|
||||
1) Provision Azure Private DNS
|
||||
2) Configure service principal for managing the zone
|
||||
3) Deploy ExternalDNS
|
||||
4) Expose an NGINX service with a LoadBalancer and annotate it with the desired DNS name
|
||||
5) Install NGINX Ingress Controller (Optional)
|
||||
6) Expose an nginx service with an ingress (Optional)
|
||||
7) Verify the DNS records
|
||||
|
||||
Everything will be deployed on Kubernetes.
|
||||
Everything will be deployed on Kubernetes.
|
||||
Therefore, please see the subsequent prerequisites.
|
||||
|
||||
## Prerequisites
|
||||
- Azure Kubernetes Service is deployed and ready
|
||||
- [Azure CLI 2.0](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) and `kubectl` installed on the box to execute the subsequent steps
|
||||
|
||||
## Install NGINX Ingress Controller
|
||||
|
||||
Helm is used to deploy the ingress controller.
|
||||
|
||||
We employ the popular chart [stable/nginx-ingress](https://github.com/helm/charts/tree/HEAD/stable/nginx-ingress).
|
||||
|
||||
```
|
||||
$ helm install stable/nginx-ingress \
|
||||
--name nginx-ingress \
|
||||
--set controller.publishService.enabled=true
|
||||
```
|
||||
|
||||
The parameter `controller.publishService.enabled` needs to be set to `true.`
|
||||
|
||||
It will make the ingress controller update the endpoint records of ingress-resources to contain the external-ip of the loadbalancer serving the ingress-controller.
|
||||
This is crucial as ExternalDNS reads those endpoints records when creating DNS-Records from ingress-resources.
|
||||
In the subsequent parameter we will make use of this. If you don't want to work with ingress-resources in your later use, you can leave the parameter out.
|
||||
|
||||
Verify the correct propagation of the loadbalancer's ip by listing the ingresses.
|
||||
|
||||
```
|
||||
$ kubectl get ingress
|
||||
```
|
||||
|
||||
The address column should contain the ip for each ingress. ExternalDNS will pick up exactly this piece of information.
|
||||
|
||||
```
|
||||
NAME HOSTS ADDRESS PORTS AGE
|
||||
nginx1 sample1.aks.com 52.167.195.110 80 6d22h
|
||||
nginx2 sample2.aks.com 52.167.195.110 80 6d21h
|
||||
```
|
||||
|
||||
If you do not want to deploy the ingress controller with Helm, ensure to pass the following cmdline-flags to it through the mechanism of your choice:
|
||||
|
||||
```
|
||||
flags:
|
||||
--publish-service=<namespace of ingress-controller >/<svcname of ingress-controller>
|
||||
--update-status=true (default-value)
|
||||
|
||||
example:
|
||||
./nginx-ingress-controller --publish-service=default/nginx-ingress-controller
|
||||
```
|
||||
- [Azure CLI 2.0](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) and `kubectl` installed on the box to execute the subsequent steps
|
||||
|
||||
## Provision Azure Private DNS
|
||||
|
||||
@ -71,7 +31,7 @@ $ az group create -n externaldns -l westeurope
|
||||
|
||||
Substitute a more suitable location for the resource group if desired.
|
||||
|
||||
As a prerequisite for Azure Private DNS to resolve records is to define links with VNETs.
|
||||
As a prerequisite for Azure Private DNS to resolve records is to define links with VNETs.
|
||||
Thus, first create a VNET.
|
||||
|
||||
```
|
||||
@ -100,11 +60,11 @@ $ az network private-dns link vnet create -g externaldns -n mylink \
|
||||
```
|
||||
|
||||
## Configure service principal for managing the zone
|
||||
ExternalDNS needs permissions to make changes in Azure Private DNS.
|
||||
ExternalDNS needs permissions to make changes in Azure Private DNS.
|
||||
These permissions are roles assigned to the service principal used by ExternalDNS.
|
||||
|
||||
A service principal with a minimum access level of `Private DNS Zone Contributor` to the Private DNS zone(s) and `Reader` to the resource group containing the Azure Private DNS zone(s) is necessary.
|
||||
More powerful role-assignments like `Owner` or assignments on subscription-level work too.
|
||||
More powerful role-assignments like `Owner` or assignments on subscription-level work too.
|
||||
|
||||
Start off by **creating the service principal** without role-assignments.
|
||||
```
|
||||
@ -118,8 +78,7 @@ $ az ad sp create-for-rbac --skip-assignment -n http://externaldns-sp
|
||||
```
|
||||
> Note: Alternatively, you can issue `az account show --query "tenantId"` to retrieve the id of your AAD Tenant too.
|
||||
|
||||
|
||||
Next, assign the roles to the service principal.
|
||||
Next, assign the roles to the service principal.
|
||||
But first **retrieve the ID's** of the objects to assign roles on.
|
||||
|
||||
```
|
||||
@ -133,17 +92,17 @@ $ az network private-dns zone show --name example.com -g externaldns --query id
|
||||
Now, **create role assignments**.
|
||||
```
|
||||
# 1. as a reader to the resource group
|
||||
$ az role assignment create --role "Reader" --assignee <appId GUID> --scope <resource group resource id>
|
||||
$ az role assignment create --role "Reader" --assignee <appId GUID> --scope <resource group resource id>
|
||||
|
||||
# 2. as a contributor to DNS Zone itself
|
||||
$ az role assignment create --role "Private DNS Zone Contributor" --assignee <appId GUID> --scope <dns zone resource id>
|
||||
$ az role assignment create --role "Private DNS Zone Contributor" --assignee <appId GUID> --scope <dns zone resource id>
|
||||
```
|
||||
|
||||
## Deploy ExternalDNS
|
||||
Configure `kubectl` to be able to communicate and authenticate with your cluster.
|
||||
Configure `kubectl` to be able to communicate and authenticate with your cluster.
|
||||
This is per default done through the file `~/.kube/config`.
|
||||
|
||||
For general background information on this see [kubernetes-docs](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/).
|
||||
For general background information on this see [kubernetes-docs](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/).
|
||||
Azure-CLI features functionality for automatically maintaining this file for AKS-Clusters. See [Azure-Docs](https://docs.microsoft.com/de-de/cli/azure/aks?view=azure-cli-latest#az-aks-get-credentials).
|
||||
|
||||
Follow the steps for [azure-dns provider](./azure.md#creating-configuration-file) to create a configuration file.
|
||||
@ -171,7 +130,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: externaldns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.8.0
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -242,7 +201,7 @@ spec:
|
||||
serviceAccountName: externaldns
|
||||
containers:
|
||||
- name: externaldns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.8.0
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -313,7 +272,7 @@ spec:
|
||||
serviceAccountName: externaldns
|
||||
containers:
|
||||
- name: externaldns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.8.0
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -337,11 +296,12 @@ Create the deployment for ExternalDNS:
|
||||
$ kubectl create -f externaldns.yaml
|
||||
```
|
||||
|
||||
## Deploying sample service
|
||||
## Create an nginx deployment
|
||||
|
||||
Create a service file called 'nginx.yaml' with the following contents:
|
||||
This step creates a demo workload in your cluster. Apply the following manifest to create a deployment that we are going to expose later in this tutorial in multiple ways:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
@ -356,15 +316,92 @@ spec:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
```
|
||||
|
||||
## Expose the nginx deployment with a load balancer
|
||||
|
||||
Apply the following manifest to create a service of type `LoadBalancer`. This will create a public load balancer in Azure that will forward traffic to the nginx pods.
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
annotations:
|
||||
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
|
||||
external-dns.alpha.kubernetes.io/hostname: server.example.com
|
||||
external-dns.alpha.kubernetes.io/internal-hostname: server-clusterip.example.com
|
||||
metadata:
|
||||
name: nginx-svc
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
protocol: TCP
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: nginx
|
||||
type: LoadBalancer
|
||||
```
|
||||
|
||||
In the service we used multiple annptations. The annotation `service.beta.kubernetes.io/azure-load-balancer-internal` is used to create an internal load balancer. The annotation `external-dns.alpha.kubernetes.io/hostname` is used to create a DNS record for the load balancer that will point to the internal IP address in the VNET allocated by the internal load balancer. The annotation `external-dns.alpha.kubernetes.io/internal-hostname` is used to create a private DNS record for the load balancer that will point to the cluster IP.
|
||||
|
||||
## Install NGINX Ingress Controller (Optional)
|
||||
|
||||
Helm is used to deploy the ingress controller.
|
||||
|
||||
We employ the popular chart [ingress-nginx](https://github.com/kubernetes/ingress-nginx/tree/main/charts/ingress-nginx).
|
||||
|
||||
```
|
||||
$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
|
||||
$ helm repo update
|
||||
$ helm install [RELEASE_NAME] ingress-nginx/ingress-nginx
|
||||
--set controller.publishService.enabled=true
|
||||
```
|
||||
|
||||
The parameter `controller.publishService.enabled` needs to be set to `true.`
|
||||
|
||||
It will make the ingress controller update the endpoint records of ingress-resources to contain the external-ip of the loadbalancer serving the ingress-controller.
|
||||
This is crucial as ExternalDNS reads those endpoints records when creating DNS-Records from ingress-resources.
|
||||
In the subsequent parameter we will make use of this. If you don't want to work with ingress-resources in your later use, you can leave the parameter out.
|
||||
|
||||
Verify the correct propagation of the loadbalancer's ip by listing the ingresses.
|
||||
|
||||
```
|
||||
$ kubectl get ingress
|
||||
```
|
||||
|
||||
The address column should contain the ip for each ingress. ExternalDNS will pick up exactly this piece of information.
|
||||
|
||||
```
|
||||
NAME HOSTS ADDRESS PORTS AGE
|
||||
nginx1 sample1.aks.com 52.167.195.110 80 6d22h
|
||||
nginx2 sample2.aks.com 52.167.195.110 80 6d21h
|
||||
```
|
||||
|
||||
If you do not want to deploy the ingress controller with Helm, ensure to pass the following cmdline-flags to it through the mechanism of your choice:
|
||||
|
||||
```
|
||||
flags:
|
||||
--publish-service=<namespace of ingress-controller >/<svcname of ingress-controller>
|
||||
--update-status=true (default-value)
|
||||
|
||||
example:
|
||||
./nginx-ingress-controller --publish-service=default/nginx-ingress-controller
|
||||
```
|
||||
|
||||
## Expose the nginx deployment with the ingress (Optional)
|
||||
|
||||
Apply the following manifest to create an ingress resource that will expose the nginx deployment. The ingress resource backend points to a `ClusterIP` service that is needed to select the pods that will receive the traffic.
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx-svc
|
||||
name: nginx-svc-clusterip
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
@ -373,7 +410,7 @@ spec:
|
||||
selector:
|
||||
app: nginx
|
||||
type: ClusterIP
|
||||
|
||||
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
@ -387,9 +424,11 @@ spec:
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: nginx-svc
|
||||
servicePort: 80
|
||||
path: /
|
||||
service:
|
||||
name: nginx-svc-clusterip
|
||||
port:
|
||||
number: 80
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
When using ExternalDNS with ingress objects it will automatically create DNS records based on host names specified in ingress objects that match the domain-filter argument in the externaldns deployment manifest. When those host names are removed or renamed the corresponding DNS records are also altered.
|
||||
@ -400,7 +439,7 @@ Create the deployment, service and ingress object:
|
||||
$ kubectl create -f nginx.yaml
|
||||
```
|
||||
|
||||
Since your external IP would have already been assigned to the nginx-ingress service, the DNS records pointing to the IP of the nginx-ingress service should be created within a minute.
|
||||
Since your external IP would have already been assigned to the nginx-ingress service, the DNS records pointing to the IP of the nginx-ingress service should be created within a minute.
|
||||
|
||||
## Verify created records
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
|
||||
# Setting up ExternalDNS for Services on Azure
|
||||
|
||||
This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster on Azure.
|
||||
This tutorial describes how to setup ExternalDNS for [Azure DNS](https://azure.microsoft.com/services/dns/) with [Azure Kubernetes Service](https://docs.microsoft.com/azure/aks/).
|
||||
|
||||
Make sure to use **>=0.5.7** version of ExternalDNS for this tutorial.
|
||||
Make sure to use **>=0.11.0** version of ExternalDNS for this tutorial.
|
||||
|
||||
This tutorial uses [Azure CLI 2.0](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) for all
|
||||
Azure commands and assumes that the Kubernetes cluster was created via Azure Container Services and `kubectl` commands
|
||||
@ -11,36 +11,29 @@ are being run on an orchestration node.
|
||||
|
||||
## Creating an Azure DNS zone
|
||||
|
||||
The Azure provider for ExternalDNS will find suitable zones for domains it manages; it will
|
||||
not automatically create zones.
|
||||
The Azure provider for ExternalDNS will find suitable zones for domains it manages; it will not automatically create zones.
|
||||
|
||||
For this tutorial, we will create a Azure resource group named 'externaldns' that can easily be deleted later:
|
||||
For this tutorial, we will create a Azure resource group named `MyDnsResourceGroup` that can easily be deleted later:
|
||||
|
||||
```
|
||||
$ az group create -n externaldns -l eastus
|
||||
```bash
|
||||
$ az group create --name "MyDnsResourceGroup" --location "eastus"
|
||||
```
|
||||
|
||||
Substitute a more suitable location for the resource group if desired.
|
||||
|
||||
Next, create a Azure DNS zone for "example.com":
|
||||
Next, create a Azure DNS zone for `example.com`:
|
||||
|
||||
```
|
||||
$ az network dns zone create -g externaldns -n example.com
|
||||
```bash
|
||||
$ az network dns zone create --resource-group "MyDnsResourceGroup" --name "example.com"
|
||||
```
|
||||
|
||||
Substitute a domain you own for "example.com" if desired.
|
||||
Substitute a domain you own for `example.com` if desired.
|
||||
|
||||
If using your own domain that was registered with a third-party domain registrar, you should point your domain's
|
||||
name servers to the values in the `nameServers` field from the JSON data returned by the `az network dns zone create` command.
|
||||
Please consult your registrar's documentation on how to do that.
|
||||
If using your own domain that was registered with a third-party domain registrar, you should point your domain's name servers to the values in the `nameServers` field from the JSON data returned by the `az network dns zone create` command. Please consult your registrar's documentation on how to do that.
|
||||
|
||||
## Permissions to modify DNS zone
|
||||
External-DNS needs permissions to make changes in the Azure DNS server. These permissions are defined in a Service Principal that should be made available to External-DNS as a configuration file.
|
||||
## Configuration file
|
||||
|
||||
The Azure DNS provider expects, by default, that the configuration file is at `/etc/kubernetes/azure.json`. This can be overridden with the `--azure-config-file` option when starting ExternalDNS.
|
||||
|
||||
### Creating configuration file
|
||||
The preferred way to inject the configuration file is by using a Kubernetes secret. The secret should contain an object named azure.json with content similar to this:
|
||||
The azure provider will reference a configuration file called `azure.json`. The preferred way to inject the configuration file is by using a Kubernetes secret. The secret should contain an object named `azure.json` with content similar to this:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -52,93 +45,93 @@ The preferred way to inject the configuration file is by using a Kubernetes secr
|
||||
}
|
||||
```
|
||||
|
||||
You can find the `tenantId` by running `az account show --query "tenantId"` or by selecting Azure Active Directory in the Azure Portal and checking the _Directory ID_ under Properties.
|
||||
The following fields are used:
|
||||
|
||||
You can find the `subscriptionId` by running `az account show --query "id"` or by selecting Subscriptions in the Azure Portal.
|
||||
* `tenantId` (**required**) - run `az account show --query "tenantId"` or by selecting Azure Active Directory in the Azure Portal and checking the _Directory ID_ under Properties.
|
||||
* `subscriptionId` (**required**) - run `az account show --query "id"` or by selecting Subscriptions in the Azure Portal.
|
||||
* `resourceGroup` (**required**) is the Resource Group created in a previous step that contains the Azure DNS Zone.
|
||||
* `aadClientID` and `aaClientSecret` are associated with the Service Principal. This is only used with Service Principal method documented in the next section.
|
||||
* `useManagedIdentityExtension` - this is set to `true` if you use either AKS Kubelet Identity or AAD Pod Identities methods documented in the next section.
|
||||
* `userAssignedIdentityID` - this contains the client id from the Managed identitty when using the AAD Pod Identities method documented in the next setion.
|
||||
|
||||
The `resourceGroup` is the Resource Group created in a previous step.
|
||||
The Azure DNS provider expects, by default, that the configuration file is at `/etc/kubernetes/azure.json`. This can be overridden with the `--azure-config-file` option when starting ExternalDNS.
|
||||
|
||||
The `aadClientID` and `aaClientSecret` are associated with the Service Principal, that you need to create next.
|
||||
## Permissions to modify DNS zone
|
||||
|
||||
### Creating service principal
|
||||
A Service Principal with a minimum access level of `DNS Zone Contributor` or `Contributor` to the DNS zone(s) and `Reader` to the resource group containing the Azure DNS zone(s) is necessary for ExternalDNS to be able to edit DNS records. However, other more permissive access levels will work too (e.g. `Contributor` to the resource group or the whole subscription).
|
||||
ExternalDNS needs permissions to make changes to the Azure DNS zone. There are three ways configure the access needed:
|
||||
|
||||
This is an Azure CLI example on how to query the Azure API for the information required for the Resource Group and DNS zone you would have already created in previous steps.
|
||||
- [Service Principal](#service-principal)
|
||||
- [Managed Identity Using AKS Kubelet Identity](#managed-identity-using-aks-kubelet-identity)
|
||||
- [Managed Identity Using AAD Pod Identities](#managed-identity-using-aad-pod-identities)
|
||||
|
||||
``` bash
|
||||
> az login
|
||||
### Service Principal
|
||||
|
||||
These permissions are defined in a Service Principal that should be made available to ExternalDNS as a configuration file `azure.json`.
|
||||
|
||||
#### Creating a service principal
|
||||
|
||||
A Service Principal with a minimum access level of `DNS Zone Contributor` or `Contributor` to the DNS zone(s) and `Reader` to the resource group containing the Azure DNS zone(s) is necessary for ExternalDNS to be able to edit DNS records. However, other more permissive access levels will work too (e.g. `Contributor` to the resource group or the whole subscription).
|
||||
|
||||
This is an Azure CLI example on how to query the Azure API for the information required for the Resource Group and DNS zone you would have already created in previous steps (requires `azure-cli` and `jq`)
|
||||
|
||||
```bash
|
||||
$ EXTERNALDNS_NEW_SP_NAME="ExternalDnsServicePrincipal" # name of the service principal
|
||||
$ AZURE_DNS_ZONE_RESOURCE_GROUP="MyDnsResourceGroup" # name of resource group where dns zone is hosted
|
||||
$ AZURE_DNS_ZONE="example.com" # DNS zone name like example.com or sub.example.com
|
||||
|
||||
# Create the service principal
|
||||
$ DNS_SP=$(az ad sp create-for-rbac --name $EXTERNALDNS_NEW_SP_NAME)
|
||||
$ EXTERNALDNS_SP_APP_ID=$(echo $DNS_SP | jq -r '.appId')
|
||||
$ EXTERNALDNS_SP_PASSWORD=$(echo $DNS_SP | jq -r '.password')
|
||||
```
|
||||
|
||||
Find the relevant subscription and make sure it is selected (the same subscriptionId should be set into azure.json)
|
||||
#### Assign the rights for the service principal
|
||||
|
||||
``` bash
|
||||
> az account list
|
||||
{
|
||||
"cloudName": "AzureCloud",
|
||||
"id": "<subscriptionId GUID>",
|
||||
"isDefault": false,
|
||||
"name": "My Subscription",
|
||||
"state": "Enabled",
|
||||
"tenantId": "AzureAD tenant ID",
|
||||
"user": {
|
||||
"name": "name",
|
||||
"type": "user"
|
||||
}
|
||||
Grant access to Azure DNS zone for the service principal.
|
||||
|
||||
# select the subscription
|
||||
> az account set -s <subscriptionId GUID>
|
||||
...
|
||||
```
|
||||
Create the service principal
|
||||
|
||||
``` bash
|
||||
> az ad sp create-for-rbac -n ExternalDnsServicePrincipal
|
||||
{
|
||||
"appId": "appId GUID", --> aadClientId value
|
||||
...
|
||||
"password": "password", --> aadClientSecret value
|
||||
"tenant": "AzureAD Tenant Id" --> tenantId value
|
||||
}
|
||||
```
|
||||
|
||||
Assign the rights for the service principal
|
||||
|
||||
```
|
||||
# find out the resource ids of the resource group where the dns zone is deployed, and the dns zone itself
|
||||
> az group show --name externaldns
|
||||
{
|
||||
"id": "/subscriptions/id/resourceGroups/externaldns",
|
||||
...
|
||||
}
|
||||
|
||||
> az network dns zone show --name example.com -g externaldns
|
||||
{
|
||||
"id": "/subscriptions/.../resourceGroups/externaldns/providers/Microsoft.Network/dnszones/example.com",
|
||||
...
|
||||
}
|
||||
```
|
||||
```
|
||||
# assign the rights to the created service principal, using the resource ids from previous step
|
||||
```bash
|
||||
# fetch DNS id used to grant access to the service principal
|
||||
DNS_ID=$(az network dns zone show --name $AZURE_DNS_ZONE \
|
||||
--resource-group $AZURE_DNS_ZONE_RESOURCE_GROUP --query "id" --output tsv)
|
||||
|
||||
# 1. as a reader to the resource group
|
||||
> az role assignment create --role "Reader" --assignee <appId GUID> --scope <resource group resource id>
|
||||
$ az role assignment create --role "Reader" --assignee $EXTERNALDNS_SP_APP_ID --scope $DNS_ID
|
||||
|
||||
# 2. as a contributor to DNS Zone itself
|
||||
> az role assignment create --role "Contributor" --assignee <appId GUID> --scope <dns zone resource id>
|
||||
|
||||
$ az role assignment create --role "Contributor" --assignee $EXTERNALDNS_SP_APP_ID --scope $DNS_ID
|
||||
```
|
||||
|
||||
Now you can create a file named 'azure.json' with values gathered above and with the structure of the example above. Use this file to create a Kubernetes secret:
|
||||
#### Creating a configuration file for the service principal
|
||||
|
||||
```
|
||||
$ kubectl create secret generic azure-config-file --from-file=/local/path/to/azure.json
|
||||
Create the file `azure.json` with values gather from previous steps.
|
||||
|
||||
```bash
|
||||
cat <<-EOF > /local/path/to/azure.json
|
||||
{
|
||||
"tenantId": "$(az account show --query tenantId -o tsv)",
|
||||
"subscriptionId": "$(az account show --query id -o tsv)",
|
||||
"resourceGroup": "$AZURE_DNS_ZONE_RESOURCE_GROUP",
|
||||
"aadClientId": "$EXTERNALDNS_SP_APP_ID",
|
||||
"aadClientSecret": "$EXTERNALDNS_SP_PASSWORD"
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
### Azure Managed Service Identity (MSI)
|
||||
Use this file to create a Kubernetes secret:
|
||||
|
||||
If [Azure Managed Service Identity (MSI)](https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/overview) is enabled for virtual machines, then there is no need to create separate service principal. Note that when granting access the kubeletidentity must be used, not the MSI used for the cluster (it usually has a name in the format <Clustername>-<agentpool>).
|
||||
```bash
|
||||
$ kubectl create secret generic azure-config-file --namespace "default" --from-file /local/path/to/azure.json
|
||||
```
|
||||
|
||||
The contents of `azure.json` should be similar to this:
|
||||
### Managed identity using AKS Kubelet identity
|
||||
|
||||
The [managed identity](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview) that is assigned to the underlying node pool in the AKS cluster can be given permissions to access Azure DNS. Managed identities are essentially a service principal whose lifecycle is managed, such as deleting the AKS cluster will also delete the service principals associated with the AKS cluster. The managed identity assigned Kuberetes node pool, or specifically the [VMSS](https://docs.microsoft.com/azure/virtual-machine-scale-sets/overview), is called the Kubelet identity.
|
||||
|
||||
The managed identites were previously called MSI (Managed Service Identity) and are enabled by default when creating an AKS cluster.
|
||||
|
||||
Note that permissions granted to this identity will be accessible to all containers running inside the Kubernetes cluster, not just the ExternalDNS container(s).
|
||||
|
||||
For the managed identity, the contents of `azure.json` should be similar to this:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -149,15 +142,184 @@ The contents of `azure.json` should be similar to this:
|
||||
}
|
||||
```
|
||||
|
||||
If you have all the information necessary: create a file called azure.json containing the json structure above and substitute the values. Otherwise create a service principal as previously shown before creating the Kubernetes secret.
|
||||
#### Fetching the Kubelet identity
|
||||
|
||||
Then add the secret to the Kubernetes cluster before continuing:
|
||||
```
|
||||
kubectl create secret generic azure-config-file --from-file=azure.json
|
||||
For this process, you will need to get the kublet identity:
|
||||
|
||||
```bash
|
||||
$ PRINCIPAL_ID=$(az aks show --resource-group $CLUSTER_GROUP --name $CLUSTERNAME \
|
||||
--query "identityProfile.kubeletidentity.objectId" --output tsv)
|
||||
```
|
||||
|
||||
#### Assign rights for the Kubelet identity
|
||||
|
||||
## Deploy ExternalDNS
|
||||
Grant access to Azure DNS zone for the kublet identity.
|
||||
|
||||
```bash
|
||||
$ AZURE_DNS_ZONE="example.com" # DNS zone name like example.com or sub.example.com
|
||||
$ AZURE_DNS_ZONE_RESOURCE_GROUP="MyDnsResourceGroup" # resource group where DNS zone is hosted
|
||||
|
||||
# fetch DNS id used to grant access to the kublet identity
|
||||
$ DNS_ID=$(az network dns zone show --name $AZURE_DNS_ZONE \
|
||||
--resource-group $AZURE_DNS_ZONE_RESOURCE_GROUP --query "id" --output tsv)
|
||||
|
||||
$ az role assignment create --role "DNS Zone Contributor" --assignee $PRINCIPAL_ID --scope $DNS_ID
|
||||
```
|
||||
|
||||
#### Creating a configuration file for the kubelet identity
|
||||
|
||||
Create the file `azure.json` with values gather from previous steps.
|
||||
|
||||
```bash
|
||||
cat <<-EOF > /local/path/to/azure.json
|
||||
{
|
||||
"tenantId": "$(az account show --query tenantId -o tsv)",
|
||||
"subscriptionId": "$(az account show --query id -o tsv)",
|
||||
"resourceGroup": "$AZURE_DNS_ZONE_RESOURCE_GROUP",
|
||||
"useManagedIdentityExtension": true
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
Use the `azure.json` file to create a Kubernetes secret:
|
||||
|
||||
```bash
|
||||
$ kubectl create secret generic azure-config-file --namespace "default" --from-file /local/path/to/azure.json
|
||||
```
|
||||
|
||||
### Managed identity using AAD Pod Identities
|
||||
|
||||
For this process, we will create a [managed identity](https://docs.microsoft.com//azure/active-directory/managed-identities-azure-resources/overview) that will be explicitly used by the ExternalDNS container. This process is similar to Kubelet identity except that this managed identity is not associated with the Kubernetes node pool, but rather associated with explicit ExternalDNS containers.
|
||||
|
||||
#### Enable the AAD Pod Identities feature
|
||||
|
||||
For this solution, [AAD Pod Identities](https://docs.microsoft.com/azure/aks/use-azure-ad-pod-identity) preview feature can be enabled. The commands below should do the trick to enable this feature:
|
||||
|
||||
```bash
|
||||
$ az feature register --name EnablePodIdentityPreview --namespace Microsoft.ContainerService
|
||||
$ az feature register --name AutoUpgradePreview --namespace Microsoft.ContainerService
|
||||
$ az extension add --name aks-preview
|
||||
$ az extension update --name aks-preview
|
||||
$ az provider register --namespace Microsoft.ContainerService
|
||||
```
|
||||
|
||||
#### Deploy the AAD Pod Identities service
|
||||
|
||||
Once enabled, you can update your cluster and install needed services for the [AAD Pod Identities](https://docs.microsoft.com/azure/aks/use-azure-ad-pod-identity) feature.
|
||||
|
||||
```bash
|
||||
$ AZURE_AKS_RESOURCE_GROUP="my-aks-cluster-group" # name of resource group where aks cluster was created
|
||||
$ AZURE_AKS_CLUSTER_NAME="my-aks-cluster" # name of aks cluster previously created
|
||||
|
||||
$ az aks update --resource-group ${AZURE_AKS_RESOURCE_GROUP} --name ${AZURE_AKS_CLUSTER_NAME} --enable-pod-identity
|
||||
```
|
||||
|
||||
Note that, if you use the default network plugin `kubenet`, then you need to add the command line option `--enable-pod-identity-with-kubenet` to the above command.
|
||||
|
||||
#### Creating the managed identity
|
||||
|
||||
After this process is finished, create a managed identity.
|
||||
|
||||
```bash
|
||||
$ IDENTITY_RESOURCE_GROUP=$AZURE_AKS_RESOURCE_GROUP # custom group or reuse AKS group
|
||||
$ IDENTITY_NAME="example-com-identity"
|
||||
|
||||
# create a managed identity
|
||||
$ az identity create --resource-group "${IDENTITY_RESOURCE_GROUP}" --name "${IDENTITY_NAME}"
|
||||
```
|
||||
|
||||
#### Assign rights for the managed identity
|
||||
|
||||
Grant access to Azure DNS zone for the managed identity.
|
||||
|
||||
```bash
|
||||
$ AZURE_DNS_ZONE_RESOURCE_GROUP="MyDnsResourceGroup" # name of resource group where dns zone is hosted
|
||||
$ AZURE_DNS_ZONE="example.com" # DNS zone name like example.com or sub.example.com
|
||||
|
||||
# fetch identity client id from managed identity created earlier
|
||||
$ IDENTITY_CLIENT_ID=$(az identity show --resource-group "${IDENTITY_RESOURCE_GROUP}" \
|
||||
--name "${IDENTITY_NAME}" --query "clientId" --output tsv)
|
||||
# fetch DNS id used to grant access to the managed identity
|
||||
$ DNS_ID=$(az network dns zone show --name "${AZURE_DNS_ZONE}" \
|
||||
--resource-group "${AZURE_DNS_ZONE_RESOURCE_GROUP}" --query "id" --output tsv)
|
||||
|
||||
$ az role assignment create --role "DNS Zone Contributor" \
|
||||
--assignee "${IDENTITY_CLIENT_ID}" --scope "${DNS_ID}"
|
||||
```
|
||||
|
||||
#### Creating a configuration file for the managed identity
|
||||
|
||||
Create the file `azure.json` with the values from previous steps:
|
||||
|
||||
```bash
|
||||
cat <<-EOF > /local/path/to/azure.json
|
||||
{
|
||||
"tenantId": "$(az account show --query tenantId -o tsv)",
|
||||
"subscriptionId": "$(az account show --query id -o tsv)",
|
||||
"resourceGroup": "$AZURE_DNS_ZONE_RESOURCE_GROUP",
|
||||
"useManagedIdentityExtension": true,
|
||||
"userAssignedIdentityID": "$IDENTITY_CLIENT_ID"
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
Use the `azure.json` file to create a Kubernetes secret:
|
||||
|
||||
```bash
|
||||
$ kubectl create secret generic azure-config-file --namespace "default" --from-file /local/path/to/azure.json
|
||||
```
|
||||
|
||||
#### Creating an Azure identity binding
|
||||
|
||||
A binding between the managed identity and the ExternalDNS pods needs to be setup by creating `AzureIdentity` and `AzureIdentityBinding` resources. This will allow appropriately labeled ExternalDNS pods to authenticate using the managed identity. When AAD Pod Identity feature is enabled from previous steps above, the `az aks pod-identity add` can be used to create these resources:
|
||||
|
||||
```bash
|
||||
$ IDENTITY_RESOURCE_ID=$(az identity show --resource-group ${IDENTITY_RESOURCE_GROUP} \
|
||||
--name ${IDENTITY_NAME} --query id --output tsv)
|
||||
|
||||
$ az aks pod-identity add --resource-group ${AZURE_AKS_RESOURCE_GROUP} \
|
||||
--cluster-name ${AZURE_AKS_CLUSTER_NAME} --namespace "default" \
|
||||
--name "external-dns" --identity-resource-id ${IDENTITY_RESOURCE_ID}
|
||||
```
|
||||
|
||||
This will add something similar to the following resouces:
|
||||
|
||||
```yaml
|
||||
apiVersion: aadpodidentity.k8s.io/v1
|
||||
kind: AzureIdentity
|
||||
metadata:
|
||||
labels:
|
||||
addonmanager.kubernetes.io/mode: Reconcile
|
||||
kubernetes.azure.com/managedby: aks
|
||||
name: external-dns
|
||||
spec:
|
||||
clientID: $IDENTITY_CLIENT_ID
|
||||
resourceID: $IDENTITY_RESOURCE_ID
|
||||
type: 0
|
||||
---
|
||||
apiVersion: aadpodidentity.k8s.io/v1
|
||||
kind: AzureIdentityBinding
|
||||
metadata:
|
||||
annotations:
|
||||
labels:
|
||||
addonmanager.kubernetes.io/mode: Reconcile
|
||||
kubernetes.azure.com/managedby: aks
|
||||
name: external-dns-binding
|
||||
spec:
|
||||
azureIdentity: external-dns
|
||||
selector: external-dns
|
||||
```
|
||||
|
||||
#### Update ExternalDNS labels
|
||||
|
||||
When deploying ExternalDNS, you want to make sure that deployed pod(s) will have the label `aadpodidbinding: external-dns` to enable AAD Pod Identities. You can patch an existing deployment of ExternalDNS with this command:
|
||||
|
||||
```bash
|
||||
kubectl patch deployment external-dns --namespace "default" --patch \
|
||||
'{"spec": {"template": {"metadata": {"labels": {"aadpodidbinding": "external-dns"}}}}}'
|
||||
```
|
||||
|
||||
## Ingress used with ExternalDNS
|
||||
|
||||
This deployment assumes that you will be using nginx-ingress. When using nginx-ingress do not deploy it as a Daemon Set. This causes nginx-ingress to write the Cluster IP of the backend pods in the ingress status.loadbalancer.ip property which then has external-dns write the Cluster IP(s) in DNS vs. the nginx-ingress service external IP.
|
||||
|
||||
@ -169,8 +331,11 @@ Ensure that your nginx-ingress deployment has the following arg: added to it:
|
||||
|
||||
For more details see here: [nginx-ingress external-dns](https://github.com/kubernetes-sigs/external-dns/blob/HEAD/docs/faq.md#why-is-externaldns-only-adding-a-single-ip-address-in-route-53-on-aws-when-using-the-nginx-ingress-controller-how-do-i-get-it-to-use-the-fqdn-of-the-elb-assigned-to-my-nginx-ingress-controller-service-instead)
|
||||
|
||||
Connect your `kubectl` client to the cluster you want to test ExternalDNS with.
|
||||
Then apply one of the following manifests file to deploy ExternalDNS.
|
||||
## Deploy ExternalDNS
|
||||
|
||||
Connect your `kubectl` client to the cluster you want to test ExternalDNS with. Then apply one of the following manifests file to deploy ExternalDNS.
|
||||
|
||||
The deployment assumes that ExternalDNS will be installed into the `default` namespace. If this namespace is different, the `ClusterRoleBinding` will need to be updated to reflect the desired alternative namespace, such as `external-dns`, `kube-addons`, etc.
|
||||
|
||||
### Manifest (for clusters without RBAC enabled)
|
||||
```yaml
|
||||
@ -191,13 +356,13 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.8.0
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
- --provider=azure
|
||||
- --azure-resource-group=externaldns # (optional) use the DNS zones from the tutorial's resource group
|
||||
- --azure-resource-group=MyDnsResourceGroup # (optional) use the DNS zones from the tutorial's resource group
|
||||
volumeMounts:
|
||||
- name: azure-config-file
|
||||
mountPath: /etc/kubernetes
|
||||
@ -206,12 +371,10 @@ spec:
|
||||
- name: azure-config-file
|
||||
secret:
|
||||
secretName: azure-config-file
|
||||
items:
|
||||
- key: externaldns-config.json
|
||||
path: azure.json
|
||||
```
|
||||
|
||||
### Manifest (for clusters with RBAC enabled, cluster access)
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
@ -223,15 +386,12 @@ kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["services","endpoints","pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions","networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["services","endpoints","pods", "nodes"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions","networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
@ -242,9 +402,9 @@ roleRef:
|
||||
kind: ClusterRole
|
||||
name: external-dns
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
namespace: default
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@ -263,26 +423,23 @@ spec:
|
||||
spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.8.0
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
- --provider=azure
|
||||
- --azure-resource-group=externaldns # (optional) use the DNS zones from the tutorial's resource group
|
||||
- --txt-prefix=externaldns-
|
||||
volumeMounts:
|
||||
- name: azure-config-file
|
||||
mountPath: /etc/kubernetes
|
||||
readOnly: true
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
- --provider=azure
|
||||
- --azure-resource-group=MyDnsResourceGroup # (optional) use the DNS zones from the tutorial's resource group
|
||||
- --txt-prefix=externaldns-
|
||||
volumeMounts:
|
||||
- name: azure-config-file
|
||||
mountPath: /etc/kubernetes
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: azure-config-file
|
||||
secret:
|
||||
secretName: azure-config-file
|
||||
items:
|
||||
- key: externaldns-config.json
|
||||
path: azure.json
|
||||
- name: azure-config-file
|
||||
secret:
|
||||
secretName: azure-config-file
|
||||
```
|
||||
|
||||
### Manifest (for clusters with RBAC enabled, namespace access)
|
||||
@ -301,12 +458,12 @@ kind: Role
|
||||
metadata:
|
||||
name: external-dns
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["services","endpoints","pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions","networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["services","endpoints","pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions","networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
@ -317,8 +474,8 @@ roleRef:
|
||||
kind: Role
|
||||
name: external-dns
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@ -337,36 +494,33 @@ spec:
|
||||
spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.8.0
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
- --provider=azure
|
||||
- --azure-resource-group=externaldns # (optional) use the DNS zones from the tutorial's resource group
|
||||
volumeMounts:
|
||||
- name: azure-config-file
|
||||
mountPath: /etc/kubernetes
|
||||
readOnly: true
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
- --provider=azure
|
||||
- --azure-resource-group=MyDnsResourceGroup # (optional) use the DNS zones from the tutorial's resource group
|
||||
volumeMounts:
|
||||
- name: azure-config-file
|
||||
mountPath: /etc/kubernetes
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: azure-config-file
|
||||
secret:
|
||||
secretName: azure-config-file
|
||||
items:
|
||||
- key: externaldns-config.json
|
||||
path: azure.json
|
||||
- name: azure-config-file
|
||||
secret:
|
||||
secretName: azure-config-file
|
||||
```
|
||||
|
||||
Create the deployment for ExternalDNS:
|
||||
|
||||
```
|
||||
$ kubectl create -f externaldns.yaml
|
||||
```bash
|
||||
$ kubectl create --namespace "default" --filename externaldns.yaml
|
||||
```
|
||||
|
||||
## Deploying an Nginx Service
|
||||
## Ingress Option: Expose an nginx service with an ingress
|
||||
|
||||
Create a service file called 'nginx.yaml' with the following contents:
|
||||
Create a file called `nginx.yaml` with the following contents:
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
@ -383,10 +537,10 @@ spec:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
@ -394,9 +548,9 @@ metadata:
|
||||
name: nginx-svc
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
protocol: TCP
|
||||
targetPort: 80
|
||||
- port: 80
|
||||
protocol: TCP
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: nginx
|
||||
type: ClusterIP
|
||||
@ -406,35 +560,80 @@ apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: nginx
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: server.example.com
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: nginx-svc
|
||||
servicePort: 80
|
||||
path: /
|
||||
- host: server.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: nginx-svc
|
||||
port:
|
||||
number: 80
|
||||
```
|
||||
|
||||
When using external-dns with ingress objects it will automatically create DNS records based on host names specified in ingress objects that match the domain-filter argument in the external-dns deployment manifest. When those host names are removed or renamed the corresponding DNS records are also altered.
|
||||
When using ExternalDNS with `ingress` objects it will automatically create DNS records based on host names specified in ingress objects that match the domain-filter argument in the external-dns deployment manifest. When those host names are removed or renamed the corresponding DNS records are also altered.
|
||||
|
||||
Create the deployment, service and ingress object:
|
||||
|
||||
```
|
||||
$ kubectl create -f nginx.yaml
|
||||
```bash
|
||||
$ kubectl create --namespace "default" --filename nginx.yaml
|
||||
```
|
||||
|
||||
Since your external IP would have already been assigned to the nginx-ingress service, the DNS records pointing to the IP of the nginx-ingress service should be created within a minute.
|
||||
Since your external IP would have already been assigned to the nginx-ingress service, the DNS records pointing to the IP of the nginx-ingress service should be created within a minute.
|
||||
|
||||
## Azure Load Balancer option: Expose an nginx service with a load balancer
|
||||
|
||||
Create a file called `nginx.yaml` with the following contents:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
annotations:
|
||||
external-dns.alpha.kubernetes.io/hostname: server.example.com
|
||||
metadata:
|
||||
name: nginx-svc
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
protocol: TCP
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: nginx
|
||||
type: LoadBalancer
|
||||
```
|
||||
|
||||
The annotation `external-dns.alpha.kubernetes.io/hostname` is used to specify the DNS name that should be created for the service. The annotation value is a comma separated list of host names.
|
||||
|
||||
## Verifying Azure DNS records
|
||||
|
||||
Run the following command to view the A records for your Azure DNS zone:
|
||||
|
||||
```
|
||||
$ az network dns record-set a list -g externaldns -z example.com
|
||||
```bash
|
||||
$ az network dns record-set a list --resource-group "MyDnsResourceGroup" --zone-name example.com
|
||||
```
|
||||
|
||||
Substitute the zone for the one created above if a different domain was used.
|
||||
@ -446,6 +645,6 @@ This should show the external IP address of the service as the A record for your
|
||||
Now that we have verified that ExternalDNS will automatically manage Azure DNS records, we can delete the tutorial's
|
||||
resource group:
|
||||
|
||||
```
|
||||
$ az group delete -n externaldns
|
||||
```bash
|
||||
$ az group delete --name "MyDnsResourceGroup"
|
||||
```
|
||||
|
@ -7,8 +7,9 @@ Install the BlueCat Gateway product and deploy the [community gateway workflows]
|
||||
|
||||
## Configuration Options
|
||||
|
||||
There are two ways to pass configuration options to the Bluecat Provider JSON configuration file and command line flags. The JSON configuration file option
|
||||
is deprecated and will eventually be removed.
|
||||
There are two ways to pass configuration options to the Bluecat Provider JSON configuration file and command line flags. Currently if a valid configuration file is used all
|
||||
BlueCat provider configurations will be taken from the configuration file. If a configuraiton file is not provided or cannot be read then all BlueCat provider configurations will
|
||||
be taken from the command line flags. In the future an enhancement will be made to merge configuration options from the configuration file and command line flags if both are provided.
|
||||
|
||||
BlueCat provider supports getting the proxy URL from the environment variables. The format is the one specified by golang's [http.ProxyFromEnvironment](https://pkg.go.dev/net/http#ProxyFromEnvironment).
|
||||
|
||||
@ -45,7 +46,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.8.0
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --log-level=debug
|
||||
- --source=service
|
||||
@ -71,7 +72,7 @@ kubectl apply -f ~/bluecat.yml -n bluecat-example
|
||||
```
|
||||
|
||||
|
||||
### Using JSON Configuration file (DEPRECATED)
|
||||
### Using JSON Configuration File
|
||||
The options for configuring the Bluecat Provider are available through the JSON file provided to External-DNS via the flag `--bluecat-config-file`.
|
||||
|
||||
| Key | Required |
|
||||
@ -135,7 +136,7 @@ spec:
|
||||
secretName: bluecatconfig
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.8.0
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
volumeMounts:
|
||||
- name: bluecatconfig
|
||||
mountPath: "/etc/external-dns/"
|
||||
|
@ -1,22 +1,20 @@
|
||||
# Setting up ExternalDNS for Services on Hetzner DNS
|
||||
# Setting up ExternalDNS for Services on Civo
|
||||
|
||||
This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using Hetzner DNS.
|
||||
This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using Civo DNS Manager.
|
||||
|
||||
Make sure to use **>=0.7.6** version of ExternalDNS for this tutorial.
|
||||
Make sure to use **>0.12.2** version of ExternalDNS for this tutorial.
|
||||
|
||||
## Creating a Hetzner DNS zone
|
||||
## Managing DNS with Civo
|
||||
|
||||
If you want to learn about how to use Hetzner's DNS service read the following tutorial series:
|
||||
If you want to learn about how to use Civo DNS Manager read the following tutorials:
|
||||
|
||||
[An Introduction to Managing DNS](https://wiki.hetzner.de/index.php/DNS_Overview), and [Add a new DNS zone](https://wiki.hetzner.de/index.php/Getting_started).
|
||||
[An Introduction to Managing DNS](https://www.civo.com/learn/configure-dns)
|
||||
|
||||
Create a new DNS zone where you want to create your records in. Let's use `example.com` as an example here.
|
||||
## Get Civo Token
|
||||
|
||||
## Creating Hetzner Credentials
|
||||
Copy the token in the settings fo your account
|
||||
|
||||
Generate a new personal token by going to [the API settings](https://dns.hetzner.com/settings/api-token) or follow [Generating an API access token](https://wiki.hetzner.de/index.php/API_access_token) if you need more information. Give the token a name and choose read and write access. The token needs to be passed to ExternalDNS so make a note of it for later use.
|
||||
|
||||
The environment variable `HETZNER_TOKEN` will be needed to run ExternalDNS with Hetzner.
|
||||
The environment variable `CIVO_TOKEN` will be needed to run ExternalDNS with Civo.
|
||||
|
||||
## Deploy ExternalDNS
|
||||
|
||||
@ -24,18 +22,18 @@ Connect your `kubectl` client to the cluster you want to test ExternalDNS with.
|
||||
Then apply one of the following manifests file to deploy ExternalDNS.
|
||||
|
||||
### Manifest (for clusters without RBAC enabled)
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
@ -43,24 +41,25 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
- --provider=hetzner
|
||||
- --provider=civo
|
||||
env:
|
||||
- name: HETZNER_TOKEN
|
||||
value: "YOUR_HETZNER_DNS_API_KEY"
|
||||
- name: CIVO_TOKEN
|
||||
value: "YOUR_CIVO_API_TOKEN"
|
||||
```
|
||||
|
||||
### Manifest (for clusters with RBAC enabled)
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
@ -69,13 +68,13 @@ rules:
|
||||
resources: ["services","endpoints","pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions","networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list","watch"]
|
||||
verbs: ["list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: external-dns-viewer
|
||||
@ -93,12 +92,11 @@ kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
@ -107,17 +105,16 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
- --provider=hetzner
|
||||
- --provider=civo
|
||||
env:
|
||||
- name: HETZNER_TOKEN
|
||||
value: "YOUR_HETZNER_DNS_API_KEY"
|
||||
- name: CIVO_TOKEN
|
||||
value: "YOUR_CIVO_API_TOKEN"
|
||||
```
|
||||
|
||||
|
||||
## Deploying an Nginx Service
|
||||
|
||||
Create a service file called 'nginx.yaml' with the following contents:
|
||||
@ -128,7 +125,6 @@ kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
@ -159,7 +155,7 @@ spec:
|
||||
targetPort: 80
|
||||
```
|
||||
|
||||
Note the annotation on the service; use the same hostname as the Hetzner DNS zone created above.
|
||||
Note the annotation on the service; use the same hostname as the Civo DNS zone created above.
|
||||
|
||||
ExternalDNS uses this annotation to determine what services should be registered with DNS. Removing the annotation will cause ExternalDNS to remove the corresponding DNS records.
|
||||
|
||||
@ -171,11 +167,11 @@ $ kubectl create -f nginx.yaml
|
||||
|
||||
Depending where you run your service it can take a little while for your cloud provider to create an external IP for the service.
|
||||
|
||||
Once the service has an external IP assigned, ExternalDNS will notice the new service IP address and synchronize the Hetzner DNS records.
|
||||
Once the service has an external IP assigned, ExternalDNS will notice the new service IP address and synchronize the Civo DNS records.
|
||||
|
||||
## Verifying Hetzner DNS records
|
||||
## Verifying Civo DNS records
|
||||
|
||||
Check your [Hetzner DNS UI](https://dns.hetzner.com/) to view the records for your Hetzner DNS zone.
|
||||
Check your [Civo UI](https://www.civo.com/account/dns) to view the records for your Civo DNS zone.
|
||||
|
||||
Click on the zone for the one created above if a different domain was used.
|
||||
|
||||
@ -183,9 +179,9 @@ This should show the external IP address of the service as the A record for your
|
||||
|
||||
## Cleanup
|
||||
|
||||
Now that we have verified that ExternalDNS will automatically manage Hetzner DNS records, we can delete the tutorial's example:
|
||||
Now that we have verified that ExternalDNS will automatically manage Civo DNS records, we can delete the tutorial's example:
|
||||
|
||||
```
|
||||
$ kubectl delete service -f nginx.yaml
|
||||
$ kubectl delete service -f externaldns.yaml
|
||||
```
|
||||
```
|
@ -18,13 +18,19 @@ Snippet from [Cloudflare - Getting Started](https://api.cloudflare.com/#getting-
|
||||
|
||||
>The Cloudflare API is a RESTful API based on HTTPS requests and JSON responses. If you are registered with Cloudflare, you can obtain your API key from the bottom of the "My Account" page, found here: [Go to My account](https://dash.cloudflare.com/profile).
|
||||
|
||||
API Token will be preferred for authentication if `CF_API_TOKEN` environment variable is set.
|
||||
API Token will be preferred for authentication if `CF_API_TOKEN` environment variable is set.
|
||||
Otherwise `CF_API_KEY` and `CF_API_EMAIL` should be set to run ExternalDNS with Cloudflare.
|
||||
You may provide the Cloudflare API token through a file by setting the
|
||||
`CF_API_TOKEN="file:/path/to/token"`.
|
||||
|
||||
When using API Token authentication, the token should be granted Zone `Read`, DNS `Edit` privileges, and access to `All zones`.
|
||||
|
||||
If you would like to further restrict the API permissions to a specific zone (or zones), you also need to use the `--zone-id-filter` so that the underlying API requests only access the zones that you explicitly specify, as opposed to accessing all zones.
|
||||
|
||||
## Throttling
|
||||
|
||||
Cloudflare API has a [global rate limit of 1,200 requests per five minutes](https://developers.cloudflare.com/fundamentals/api/reference/limits/). Running several fast polling ExternalDNS instances in a given account can easily hit that limit. The AWS Provider [docs](./aws.md#throttling) has some recommendations that can be followed here too, but in particular, consider passing `--cloudflare-dns-records-per-page` with a high value (maximum is 5,000).
|
||||
|
||||
## Deploy ExternalDNS
|
||||
|
||||
Connect your `kubectl` client to the cluster you want to test ExternalDNS with.
|
||||
@ -50,13 +56,14 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
- --zone-id-filter=023e105f4ecef8ad9ca31a8372d0c353 # (optional) limit to a specific zone.
|
||||
- --provider=cloudflare
|
||||
- --cloudflare-proxied # (optional) enable the proxy feature of Cloudflare (DDOS protection, CDN...)
|
||||
- --cloudflare-dns-records-per-page=5000 # (optional) configure how many DNS records to fetch per request
|
||||
env:
|
||||
- name: CF_API_KEY
|
||||
value: "YOUR_CLOUDFLARE_API_KEY"
|
||||
@ -81,7 +88,7 @@ rules:
|
||||
resources: ["services","endpoints","pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions","networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
@ -118,13 +125,14 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
- --zone-id-filter=023e105f4ecef8ad9ca31a8372d0c353 # (optional) limit to a specific zone.
|
||||
- --provider=cloudflare
|
||||
- --cloudflare-proxied # (optional) enable the proxy feature of Cloudflare (DDOS protection, CDN...)
|
||||
- --cloudflare-dns-records-per-page=5000 # (optional) configure how many DNS records to fetch per request
|
||||
env:
|
||||
- name: CF_API_KEY
|
||||
value: "YOUR_CLOUDFLARE_API_KEY"
|
||||
|
@ -26,7 +26,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -102,7 +102,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
|
@ -108,7 +108,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=ingress
|
||||
- --provider=coredns
|
||||
@ -175,7 +175,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=ingress
|
||||
- --provider=coredns
|
||||
|
@ -59,7 +59,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -136,7 +136,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -43,7 +43,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -107,7 +107,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -35,7 +35,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone you create in DNSimple.
|
||||
@ -100,7 +100,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone you create in DNSimple.
|
||||
|
@ -43,7 +43,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=ingress
|
||||
- --txt-prefix=_d
|
||||
@ -122,9 +122,11 @@ spec:
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: my-awesome-service
|
||||
servicePort: 8080
|
||||
|
||||
service:
|
||||
name: my-awesome-service
|
||||
port:
|
||||
number: 8080
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
As the DNS name `test-ingress.example.com` matches the filter, external-dns will create two records:
|
||||
|
@ -41,7 +41,7 @@ spec:
|
||||
# serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=ingress # or service or both
|
||||
- --provider=exoscale
|
||||
@ -117,8 +117,11 @@ spec:
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: nginx
|
||||
servicePort: 80
|
||||
service:
|
||||
name: "nginx"
|
||||
port:
|
||||
number: 80
|
||||
pathType: Prefix
|
||||
|
||||
---
|
||||
|
||||
|
@ -27,7 +27,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --log-level=debug
|
||||
- --source=service
|
||||
|
33
docs/tutorials/f5-virtualserver.md
Normal file
33
docs/tutorials/f5-virtualserver.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Configuring ExternalDNS to use the F5 Networks VirtualServer Source
|
||||
This tutorial describes how to configure ExternalDNS to use the F5 Networks VirtualServer Source. It is meant to supplement the other provider-specific setup tutorials.
|
||||
|
||||
The F5 Networks VirtualServer CRD is part of [this](https://github.com/F5Networks/k8s-bigip-ctlr) project. See more in-depth info regarding the VirtualServer CRD [here](https://github.com/F5Networks/k8s-bigip-ctlr/blob/master/docs/config_examples/customResource/CustomResource.md#virtualserver).
|
||||
|
||||
## Start with ExternalDNS with the F5 Networks VirtualServer source
|
||||
|
||||
1. Make sure that you have the `k8s-bigip-ctlr` installed in your cluster. The needed CRDs are bundled within the controller.
|
||||
|
||||
2. In your Helm `values.yaml` add:
|
||||
```
|
||||
sources:
|
||||
- ...
|
||||
- f5-virtualserver
|
||||
- ...
|
||||
```
|
||||
or add it in your `Deployment` if you aren't installing `external-dns` via Helm:
|
||||
```
|
||||
args:
|
||||
- --source=f5-virtualserver
|
||||
```
|
||||
|
||||
Note that, in case you're not installing via Helm, you'll need the following in the `ClusterRole` bound to the service account of `external-dns`:
|
||||
```
|
||||
- apiGroups:
|
||||
- cis.f5.com
|
||||
resources:
|
||||
- virtualservers
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
```
|
@ -39,7 +39,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.7
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -103,7 +103,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.7
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
96
docs/tutorials/gateway-api.md
Normal file
96
docs/tutorials/gateway-api.md
Normal file
@ -0,0 +1,96 @@
|
||||
# Configuring ExternalDNS to use Gateway API Route Sources
|
||||
|
||||
This describes how to configure ExternalDNS to use Gateway API Route sources.
|
||||
It is meant to supplement the other provider-specific setup tutorials.
|
||||
|
||||
## Supported API Versions
|
||||
|
||||
As the Gateway API is still in an experimental phase, ExternalDNS makes no backwards
|
||||
compatibilty guarantees regarding its support. However, it currently supports a mixture of
|
||||
v1alpha2 and v1beta1 APIs. Gateways and HTTPRoutes are supported using the v1beta1 API.
|
||||
GRPCRoutes, TLSRoutes, TCPRoutes, and UDPRoutes are supported using the v1alpha2 API.
|
||||
|
||||
## Hostnames
|
||||
|
||||
HTTPRoute and TLSRoute specs, along with their associated Gateway Listeners, contain hostnames that
|
||||
will be used by ExternalDNS. However, no such hostnames may be specified in TCPRoute or UDPRoute
|
||||
specs. For TCPRoutes and UDPRoutes, the `external-dns.alpha.kubernetes.io/hostname` annotation
|
||||
is the recommended way to provide their hostnames to ExternalDNS. This annotation is also supported
|
||||
for HTTPRoutes and TLSRoutes by ExternalDNS, but it's _strongly_ recommended that they use their
|
||||
specs to provide all intended hostnames, since the Gateway that ultimately routes their
|
||||
requests/connections won't recognize additional hostnames from the annotation.
|
||||
|
||||
## Manifest with RBAC
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["namespaces"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["gateway.networking.k8s.io"]
|
||||
resources: ["gateways","httproutes","grpcroutes","tlsroutes","tcproutes","udproutes"]
|
||||
verbs: ["get","watch","list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: external-dns
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: external-dns
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
namespace: default
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
# Add desired Gateway API Route sources.
|
||||
- --source=gateway-httproute
|
||||
- --source=gateway-grpcroute
|
||||
- --source=gateway-tlsroute
|
||||
- --source=gateway-tcproute
|
||||
- --source=gateway-udproute
|
||||
# Optionally, limit Routes to those in the given namespace.
|
||||
- --namespace=my-route-namespace
|
||||
# Optionally, limit Routes to those matching the given label selector.
|
||||
- --label-filter=my-route-label==my-route-value
|
||||
# Optionally, limit Route endpoints to those Gateways in the given namespace.
|
||||
- --gateway-namespace=my-gateway-namespace
|
||||
# Optionally, limit Route endpoints to those Gateways matching the given label selector.
|
||||
- --gateway-label-filter=my-gateway-label==my-gateway-value
|
||||
# Add provider-specific flags...
|
||||
- --domain-filter=external-dns-test.my-org.com
|
||||
- --provider=google
|
||||
- --registry=txt
|
||||
- --txt-owner-id=my-identifier
|
||||
```
|
@ -1,66 +1,264 @@
|
||||
# Setting up ExternalDNS on Google Kubernetes Engine
|
||||
|
||||
This tutorial describes how to setup ExternalDNS for usage within a GKE cluster. Make sure to use **>=0.4** version of ExternalDNS for this tutorial
|
||||
This tutorial describes how to setup ExternalDNS for usage within a [GKE](https://cloud.google.com/kubernetes-engine) ([Google Kuberentes Engine](https://cloud.google.com/kubernetes-engine)) cluster. Make sure to use **>=0.11.0** version of ExternalDNS for this tutorial
|
||||
|
||||
## Set up your environment
|
||||
|
||||
Setup your environment to work with Google Cloud Platform. Fill in your values as needed, e.g. target project.
|
||||
|
||||
```console
|
||||
$ gcloud config set project "zalando-external-dns-test"
|
||||
$ gcloud config set compute/region "europe-west1"
|
||||
$ gcloud config set compute/zone "europe-west1-d"
|
||||
```
|
||||
|
||||
## GKE Node Scopes
|
||||
## Single project test scenario using access scopes
|
||||
|
||||
*If you prefer to try-out ExternalDNS in one of the existing environments you can skip this step*
|
||||
|
||||
The following instructions use instance scopes to provide ExternalDNS with the
|
||||
permissions it needs to manage DNS records. Note that since these permissions
|
||||
are associated with the instance, all pods in the cluster will also have these
|
||||
permissions. As such, this approach is not suitable for anything but testing
|
||||
environments.
|
||||
The following instructions use [access scopes](https://cloud.google.com/compute/docs/access/service-accounts#accesscopesiam) to provide ExternalDNS with the permissions it needs to manage DNS records within a single [project](https://cloud.google.com/docs/overview#projects), the organizing entity to allocate resources.
|
||||
|
||||
Create a GKE cluster.
|
||||
Note that since these permissions are associated with the instance, all pods in the cluster will also have these permissions. As such, this approach is not suitable for anything but testing environments.
|
||||
|
||||
```console
|
||||
$ gcloud container clusters create "external-dns" \
|
||||
--num-nodes 1 \
|
||||
--scopes "https://www.googleapis.com/auth/ndev.clouddns.readwrite"
|
||||
This solution will only work when both CloudDNS and GKE are provisioned in the same project. If the CloudDNS zone is in a different project, this solution will not work.
|
||||
|
||||
### Configure Project Environment
|
||||
|
||||
Setup your environment to work with Google Cloud Platform. Fill in your variables as needed, e.g. target project.
|
||||
|
||||
```bash
|
||||
# set variables to the appropriate desired values
|
||||
PROJECT_ID="my-external-dns-test"
|
||||
REGION="europe-west1"
|
||||
ZONE="europe-west1-d"
|
||||
ClOUD_BILLING_ACCOUNT="<my-cloud-billing-account>"
|
||||
# set default settings for project
|
||||
gcloud config set project $PROJECT_ID
|
||||
gcloud config set compute/region $REGION
|
||||
gcloud config set compute/zone $ZONE
|
||||
# enable billing and APIs if not done already
|
||||
gcloud beta billing projects link $PROJECT_ID \
|
||||
--billing-account $BILLING_ACCOUNT
|
||||
gcloud services enable "dns.googleapis.com"
|
||||
gcloud services enable "container.googleapis.com"
|
||||
```
|
||||
|
||||
Create a DNS zone which will contain the managed DNS records.
|
||||
### Create GKE Cluster
|
||||
|
||||
```console
|
||||
$ gcloud dns managed-zones create "external-dns-test-gcp-zalan-do" \
|
||||
--dns-name "external-dns-test.gcp.zalan.do." \
|
||||
--description "Automatically managed zone by kubernetes.io/external-dns"
|
||||
```bash
|
||||
gcloud container clusters create $GKE_CLUSTER_NAME \
|
||||
--num-nodes 1 \
|
||||
--scopes "https://www.googleapis.com/auth/ndev.clouddns.readwrite"
|
||||
```
|
||||
|
||||
**WARNING**: Note that this cluster will use the default [compute engine GSA](https://cloud.google.com/compute/docs/access/service-accounts#default_service_account) that contians the overly permissive project editor (`roles/editor`) role. So essentially, anything on the cluster could potentially grant escalated privileges. Also, as mentioned earlier, the access scope `ndev.clouddns.readwrite` will allow anything running on the cluster to have read/write permissions on all Cloud DNS zones within the same project.
|
||||
|
||||
### Cloud DNS Zone
|
||||
|
||||
Create a DNS zone which will contain the managed DNS records. If using your own domain that was registered with a third-party domain registrar, you should point your domain's name servers to the values under the `nameServers` key. Please consult your registrar's documentation on how to do that. This tutorial will use example domain of `example.com`.
|
||||
|
||||
```bash
|
||||
gcloud dns managed-zones create "example-com" --dns-name "example.com." \
|
||||
--description "Automatically managed zone by kubernetes.io/external-dns"
|
||||
```
|
||||
|
||||
Make a note of the nameservers that were assigned to your new zone.
|
||||
|
||||
```console
|
||||
$ gcloud dns record-sets list \
|
||||
--zone "external-dns-test-gcp-zalan-do" \
|
||||
--name "external-dns-test.gcp.zalan.do." \
|
||||
--type NS
|
||||
NAME TYPE TTL DATA
|
||||
external-dns-test.gcp.zalan.do. NS 21600 ns-cloud-e1.googledomains.com.,ns-cloud-e2.googledomains.com.,ns-cloud-e3.googledomains.com.,ns-cloud-e4.googledomains.com.
|
||||
```bash
|
||||
gcloud dns record-sets list \
|
||||
--zone "example-com" --name "example.com." --type NS
|
||||
```
|
||||
|
||||
Outputs:
|
||||
|
||||
```
|
||||
NAME TYPE TTL DATA
|
||||
example.com. NS 21600 ns-cloud-e1.googledomains.com.,ns-cloud-e2.googledomains.com.,ns-cloud-e3.googledomains.com.,ns-cloud-e4.googledomains.com.
|
||||
```
|
||||
|
||||
In this case it's `ns-cloud-{e1-e4}.googledomains.com.` but your's could slightly differ, e.g. `{a1-a4}`, `{b1-b4}` etc.
|
||||
|
||||
Tell the parent zone where to find the DNS records for this zone by adding the corresponding NS records there. Assuming the parent zone is "gcp-zalan-do" and the domain is "gcp.zalan.do" and that it's also hosted at Google we would do the following.
|
||||
## Cross project access scenario using Google Service Account
|
||||
|
||||
```console
|
||||
$ gcloud dns record-sets transaction start --zone "gcp-zalan-do"
|
||||
$ gcloud dns record-sets transaction add ns-cloud-e{1..4}.googledomains.com. \
|
||||
--name "external-dns-test.gcp.zalan.do." --ttl 300 --type NS --zone "gcp-zalan-do"
|
||||
$ gcloud dns record-sets transaction execute --zone "gcp-zalan-do"
|
||||
More often, following best practices in regards to security and operations, Cloud DNS zones will be managed in a separate project from the Kubernetes cluster. This section shows how setup ExternalDNS to access Cloud DNS from a different project. These steps will also work for single project scenarios as well.
|
||||
|
||||
ExternalDNS will need permissions to make changes to the Cloud DNS zone. There are three ways to configure the access needed:
|
||||
|
||||
* [Worker Node Service Account](#worker-node-service-account)
|
||||
* [Static Credentials](#static-credentials)
|
||||
* [Work Load Identity](#work-load-identity)
|
||||
|
||||
### Setup Cloud DNS and GKE
|
||||
|
||||
Below are examples on how you can configure Cloud DNS and GKE in separate projects, and then use one of the three methods to grant access to ExternalDNS. Replace the environment variables to values that make sense in your environment.
|
||||
|
||||
#### Configure Projects
|
||||
|
||||
For this process, create projects with the appropriate APIs enabled.
|
||||
|
||||
```bash
|
||||
# set variables to appropriate desired values
|
||||
GKE_PROJECT_ID="my-workload-project"
|
||||
DNS_PROJECT_ID="my-cloud-dns-project"
|
||||
ClOUD_BILLING_ACCOUNT="<my-cloud-billing-account>"
|
||||
# enable billing and APIs for DNS project if not done already
|
||||
gcloud config set project $DNS_PROJECT_ID
|
||||
gcloud beta billing projects link $CLOUD_DNS_PROJECT \
|
||||
--billing-account $ClOUD_BILLING_ACCOUNT
|
||||
gcloud services enable "dns.googleapis.com"
|
||||
# enable billing and APIs for GKE project if not done already
|
||||
gcloud config set project $GKE_PROJECT_ID
|
||||
gcloud beta billing projects link $CLOUD_DNS_PROJECT \
|
||||
--billing-account $ClOUD_BILLING_ACCOUNT
|
||||
gcloud services enable "container.googleapis.com"
|
||||
```
|
||||
|
||||
### Deploy ExternalDNS
|
||||
#### Provisioning Cloud DNS
|
||||
|
||||
Create a Cloud DNS zone in the designated DNS project.
|
||||
|
||||
```bash
|
||||
gcloud dns managed-zones create "example-com" --project $DNS_PROJECT_ID \
|
||||
--description "example.com" --dns-name="example.com." --visibility=public
|
||||
```
|
||||
|
||||
If using your own domain that was registered with a third-party domain registrar, you should point your domain's name servers to the values under the `nameServers` key. Please consult your registrar's documentation on how to do that. The example domain of `example.com` will be used for this tutorial.
|
||||
|
||||
#### Provisioning a GKE cluster for cross project access
|
||||
|
||||
Create a GSA (Google Service Account) and grant it the [minimal set of privileges required](https://cloud.google.com/kubernetes-engine/docs/how-to/hardening-your-cluster#use_least_privilege_sa) for GKE nodes:
|
||||
|
||||
```bash
|
||||
GKE_CLUSTER_NAME="my-external-dns-cluster"
|
||||
GKE_REGION="us-central1"
|
||||
GKE_SA_NAME="worker-nodes-sa"
|
||||
GKE_SA_EMAIL="$GKE_SA_NAME@${GKE_PROJECT_ID}.iam.gserviceaccount.com"
|
||||
|
||||
ROLES=(
|
||||
roles/logging.logWriter
|
||||
roles/monitoring.metricWriter
|
||||
roles/monitoring.viewer
|
||||
roles/stackdriver.resourceMetadata.writer
|
||||
)
|
||||
|
||||
gcloud iam service-accounts create $GKE_SA_NAME \
|
||||
--display-name $GKE_SA_NAME --project $GKE_PROJECT_ID
|
||||
|
||||
# assign google service account to roles in GKE project
|
||||
for ROLE in ${ROLES[*]}; do
|
||||
gcloud projects add-iam-policy-binding $GKE_PROJECT_ID \
|
||||
--member "serviceAccount:$GKE_SA_EMAIL" \
|
||||
--role $ROLE
|
||||
done
|
||||
```
|
||||
|
||||
Create a cluster using this service account and enable [workload identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity):
|
||||
|
||||
```bash
|
||||
gcloud container clusters create $GKE_CLUSTER_NAME \
|
||||
--project $GKE_PROJECT_ID --region $GKE_REGION --num-nodes 1 \
|
||||
--service-account "$GKE_SA_EMAIL" \
|
||||
--workload-pool "$GKE_PROJECT_ID.svc.id.goog"
|
||||
```
|
||||
|
||||
### Worker Node Service Account method
|
||||
|
||||
In this method, the GSA (Google Service Account) that is associated with GKE worker nodes will be configured to have access to Cloud DNS.
|
||||
|
||||
**WARNING**: This will grant access to modify the Cloud DNS zone records for all containers running on cluster, not just ExternalDNS, so use this option with caution. This is not recommended for production environments.
|
||||
|
||||
```bash
|
||||
GKE_SA_EMAIL="$GKE_SA_NAME@${GKE_PROJECT_ID}.iam.gserviceaccount.com"
|
||||
|
||||
# assign google service account to dns.admin role in the cloud dns project
|
||||
gcloud projects add-iam-policy-binding $DNS_PROJECT_ID \
|
||||
--member serviceAccount:$GKE_SA_EMAIL \
|
||||
--role roles/dns.admin
|
||||
```
|
||||
|
||||
After this, follow the steps in [Deploy ExternalDNS](#deploy-externaldns). Make sure to set the `--google-project` flag to match the Cloud DNS project name.
|
||||
|
||||
### Static Credentials
|
||||
|
||||
In this scenario, a new GSA (Google Service Account) is created that has access to the CloudDNS zone. The credentials for this GSA are saved and installed as a Kubernetes secret that will be used by ExternalDNS.
|
||||
|
||||
This allows only containers that have access to the secret, such as ExternalDNS to update records on the Cloud DNS Zone.
|
||||
|
||||
#### Create GSA for use with static credentials
|
||||
|
||||
```bash
|
||||
DNS_SA_NAME="external-dns-sa"
|
||||
DNS_SA_EMAIL="$DNS_SA_NAME@${GKE_PROJECT_ID}.iam.gserviceaccount.com"
|
||||
|
||||
# create GSA used to access the Cloud DNS zone
|
||||
gcloud iam service-accounts create $DNS_SA_NAME --display-name $DNS_SA_NAME
|
||||
|
||||
# assign google service account to dns.admin role in cloud-dns project
|
||||
gcloud projects add-iam-policy-binding $DNS_PROJECT_ID \
|
||||
--member serviceAccount:$DNS_SA_EMAIL --role "roles/dns.admin"
|
||||
```
|
||||
|
||||
#### Create Kubernetes secret using static credentials
|
||||
|
||||
Generate static credentials from the ExternalDNS GSA.
|
||||
|
||||
```bash
|
||||
# download static credentials
|
||||
gcloud iam service-accounts keys create /local/path/to/credentials.json \
|
||||
--iam-account $DNS_SA_EMAIL
|
||||
```
|
||||
|
||||
Create a Kubernetes secret with the credentials in the same namespace of ExternalDNS.
|
||||
|
||||
```bash
|
||||
kubectl create secret generic "external-dns" --namespace ${EXTERNALDNS_NS:-"default"} \
|
||||
--from-file /local/path/to/credentials.json
|
||||
```
|
||||
|
||||
After this, follow the steps in [Deploy ExternalDNS](#deploy-externaldns). Make sure to set the `--google-project` flag to match Cloud DNS project name. Make sure to uncomment out the section that mounts the secret to the ExternalDNS pods.
|
||||
### Workload Identity
|
||||
|
||||
[Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) allows workloads in your GKE cluster to impersonate GSA (Google Service Accounts) using KSA (Kubernetes Service Accounts) configured during deployemnt. These are the steps to use this feature with ExternalDNS.
|
||||
|
||||
#### Create GSA for use with Workload Identity
|
||||
|
||||
```bash
|
||||
DNS_SA_NAME="external-dns-sa"
|
||||
DNS_SA_EMAIL="$DNS_SA_NAME@${GKE_PROJECT_ID}.iam.gserviceaccount.com"
|
||||
|
||||
gcloud iam service-accounts create $DNS_SA_NAME --display-name $DNS_SA_NAME
|
||||
gcloud projects add-iam-policy-binding $DNS_PROJECT_ID \
|
||||
--member serviceAccount:$DNS_SA_EMAIL --role "roles/dns.admin"
|
||||
```
|
||||
|
||||
#### Link KSA to GSA
|
||||
|
||||
Add an IAM policy binding bewtween the workload identity GSA and ExternalDNS GSA. This will link the ExternalDNS KSA to ExternalDNS GSA.
|
||||
|
||||
```bash
|
||||
gcloud iam service-accounts add-iam-policy-binding $DNS_SA_EMAIL \
|
||||
--role "roles/iam.workloadIdentityUser" \
|
||||
--member "serviceAccount:$GKE_PROJECT_ID.svc.id.goog[${EXTERNALDNS_NS:-"default"}/external-dns]"
|
||||
```
|
||||
|
||||
#### Deploy External DNS
|
||||
|
||||
Deploy ExternalDNS with the following steps below, documented under [Deploy ExternalDNS](#deploy-externaldns). Set the `--google-project` flag to the Cloud DNS project name.
|
||||
|
||||
#### Link KSA to GSA in Kubernetes
|
||||
|
||||
Add the proper workload identity annotation to the ExternalDNS KSA.
|
||||
|
||||
```bash
|
||||
kubectl annotate serviceaccount "external-dns" \
|
||||
--namespace ${EXTERNALDNS_NS:-"default"} \
|
||||
"iam.gke.io/gcp-service-account=$DNS_SA_EMAIL"
|
||||
```
|
||||
|
||||
#### Update ExternalDNS pods
|
||||
|
||||
Update the Pod spec to schedule the workloads on nodes that use Workload Identity and to use the annotated Kubernetes service account.
|
||||
|
||||
```bash
|
||||
kubectl patch deployment "external-dns" \
|
||||
--namespace ${EXTERNALDNS_NS:-"default"} \
|
||||
--patch \
|
||||
'{"spec": {"template": {"spec": {"nodeSelector": {"iam.gke.io/gke-metadata-server-enabled": "true"}}}}}'
|
||||
```
|
||||
|
||||
After all of these steps you may see several messages with `googleapi: Error 403: Forbidden, forbidden`. After several minutes when the token is refreshed, these error messages will go away, and you should see info messages, such as: `All records are already up to date`.
|
||||
|
||||
## Deploy ExternalDNS
|
||||
|
||||
Then apply the following manifests file to deploy ExternalDNS.
|
||||
|
||||
@ -69,71 +267,96 @@ apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
labels:
|
||||
app.kubernetes.io/name: external-dns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
labels:
|
||||
app.kubernetes.io/name: external-dns
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["services","endpoints","pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions","networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["get", "watch", "list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["services","endpoints","pods","nodes"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions","networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: external-dns-viewer
|
||||
labels:
|
||||
app.kubernetes.io/name: external-dns
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: external-dns
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
namespace: default
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
namespace: default # change if namespace is not 'default'
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
labels:
|
||||
app.kubernetes.io/name: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
app.kubernetes.io/name: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
app.kubernetes.io/name: external-dns
|
||||
spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.8.0
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --domain-filter=external-dns-test.gcp.zalan.do # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
|
||||
- --provider=google
|
||||
# - --google-project=zalando-external-dns-test # Use this to specify a project different from the one external-dns is running inside
|
||||
- --google-zone-visibility=private # Use this to filter to only zones with this visibility. Set to either 'public' or 'private'. Omitting will match public and private zones
|
||||
- --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
|
||||
- --registry=txt
|
||||
- --txt-owner-id=my-identifier
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --domain-filter=example.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
|
||||
- --provider=google
|
||||
- --log-format=json # google cloud logs parses severity of the "text" log format incorrectly
|
||||
# - --google-project=my-cloud-dns-project # Use this to specify a project different from the one external-dns is running inside
|
||||
- --google-zone-visibility=public # Use this to filter to only zones with this visibility. Set to either 'public' or 'private'. Omitting will match public and private zones
|
||||
- --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
|
||||
- --registry=txt
|
||||
- --txt-owner-id=my-identifier
|
||||
# # uncomment below if static credentials are used
|
||||
# env:
|
||||
# - name: GOOGLE_APPLICATION_CREDENTIALS
|
||||
# value: /etc/secrets/service-account/credentials.json
|
||||
# volumeMounts:
|
||||
# - name: google-service-account
|
||||
# mountPath: /etc/secrets/service-account/
|
||||
# volumes:
|
||||
# - name: google-service-account
|
||||
# secret:
|
||||
# secretName: external-dns
|
||||
```
|
||||
|
||||
Use `--dry-run` if you want to be extra careful on the first run. Note, that you will not see any records created when you are running in dry-run mode. You can, however, inspect the logs and watch what would have been done.
|
||||
Create the deployment for ExternalDNS:
|
||||
|
||||
### Verify ExternalDNS works
|
||||
```bash
|
||||
kubectl create --namespace "default" --filename externaldns.yaml
|
||||
```
|
||||
|
||||
Create the following sample application to test that ExternalDNS works.
|
||||
## Verify ExternalDNS works
|
||||
|
||||
The following will deploy a small nginx server that will be used to demonstrate that ExternalDNS is working.
|
||||
|
||||
### Verify using an external load balancer
|
||||
|
||||
Create the following sample application to test that ExternalDNS works. This example will provision a L4 load balancer.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
@ -141,17 +364,16 @@ kind: Service
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.gcp.zalan.do.
|
||||
# change nginx.example.com to match an appropriate value
|
||||
external-dns.alpha.kubernetes.io/hostname: nginx.example.com
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: nginx
|
||||
|
||||
---
|
||||
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
@ -166,49 +388,49 @@ spec:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
```
|
||||
|
||||
Create the deployment and service objects:
|
||||
|
||||
```bash
|
||||
kubectl create --namespace "default" --filename nginx.yaml
|
||||
```
|
||||
|
||||
After roughly two minutes check that a corresponding DNS record for your service was created.
|
||||
|
||||
```console
|
||||
$ gcloud dns record-sets list \
|
||||
--zone "external-dns-test-gcp-zalan-do" \
|
||||
--name "nginx.external-dns-test.gcp.zalan.do."
|
||||
|
||||
NAME TYPE TTL DATA
|
||||
nginx.external-dns-test.gcp.zalan.do. A 300 104.155.60.49
|
||||
nginx.external-dns-test.gcp.zalan.do. TXT 300 "heritage=external-dns,external-dns/owner=my-identifier"
|
||||
```bash
|
||||
gcloud dns record-sets list --zone "example-com" --name "nginx.example.com."
|
||||
```
|
||||
|
||||
Note created TXT record alongside A record. TXT record signifies that the corresponding A record is managed by ExternalDNS. This makes ExternalDNS safe for running in environments where there are other records managed via other means.
|
||||
Example output:
|
||||
|
||||
```
|
||||
NAME TYPE TTL DATA
|
||||
nginx.example.com. A 300 104.155.60.49
|
||||
nginx.example.com. TXT 300 "heritage=external-dns,external-dns/owner=my-identifier"
|
||||
```
|
||||
|
||||
Note created `TXT` record alongside `A` record. `TXT` record signifies that the corresponding `A` record is managed by ExternalDNS. This makes ExternalDNS safe for running in environments where there are other records managed via other means.
|
||||
|
||||
Let's check that we can resolve this DNS name. We'll ask the nameservers assigned to your zone first.
|
||||
|
||||
```console
|
||||
$ dig +short @ns-cloud-e1.googledomains.com. nginx.external-dns-test.gcp.zalan.do.
|
||||
```bash
|
||||
dig +short @ns-cloud-e1.googledomains.com. nginx.example.com.
|
||||
104.155.60.49
|
||||
```
|
||||
|
||||
Given you hooked up your DNS zone with its parent zone you can use `curl` to access your site.
|
||||
|
||||
```console
|
||||
$ curl nginx.external-dns-test.gcp.zalan.do
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Welcome to nginx!</title>
|
||||
...
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
```bash
|
||||
curl nginx.example.com
|
||||
```
|
||||
|
||||
### Verify using an ingress
|
||||
|
||||
Let's check that Ingress works as well. Create the following Ingress.
|
||||
|
||||
```yaml
|
||||
@ -218,362 +440,66 @@ metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
rules:
|
||||
- host: via-ingress.external-dns-test.gcp.zalan.do
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: nginx
|
||||
servicePort: 80
|
||||
- host: server.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: nginx
|
||||
port:
|
||||
number: 80
|
||||
```
|
||||
|
||||
Again, after roughly two minutes check that a corresponding DNS record for your Ingress was created.
|
||||
Create the ingress objects with:
|
||||
|
||||
```console
|
||||
$ gcloud dns record-sets list \
|
||||
--zone "external-dns-test-gcp-zalan-do" \
|
||||
--name "via-ingress.external-dns-test.gcp.zalan.do." \
|
||||
```bash
|
||||
kubectl create --namespace "default" --filename ingress.yaml
|
||||
```
|
||||
|
||||
NAME TYPE TTL DATA
|
||||
via-ingress.external-dns-test.gcp.zalan.do. A 300 130.211.46.224
|
||||
via-ingress.external-dns-test.gcp.zalan.do. TXT 300 "heritage=external-dns,external-dns/owner=my-identifier"
|
||||
Note that this will ingress object will use the default ingress controller that comes with GKE to create a L7 load balancer in addition to the L4 load balancer previously with the service object. To use only the L7 load balancer, update the service manafest to change the Service type to `NodePort` and remove the ExternalDNS annotation.
|
||||
|
||||
After roughly two minutes check that a corresponding DNS record for your Ingress was created.
|
||||
|
||||
```bash
|
||||
gcloud dns record-sets list \
|
||||
--zone "example-com" \
|
||||
--name "server.example.com." \
|
||||
```
|
||||
Output:
|
||||
|
||||
```
|
||||
NAME TYPE TTL DATA
|
||||
server.example.com. A 300 130.211.46.224
|
||||
server.example.com. TXT 300 "heritage=external-dns,external-dns/owner=my-identifier"
|
||||
```
|
||||
|
||||
Let's check that we can resolve this DNS name as well.
|
||||
|
||||
```console
|
||||
dig +short @ns-cloud-e1.googledomains.com. via-ingress.external-dns-test.gcp.zalan.do.
|
||||
```bash
|
||||
dig +short @ns-cloud-e1.googledomains.com. server.example.com.
|
||||
130.211.46.224
|
||||
```
|
||||
|
||||
Try with `curl` as well.
|
||||
|
||||
```console
|
||||
$ curl via-ingress.external-dns-test.gcp.zalan.do
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Welcome to nginx!</title>
|
||||
...
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
```bash
|
||||
curl server.example.com
|
||||
```
|
||||
|
||||
### Clean up
|
||||
|
||||
Make sure to delete all Service and Ingress objects before terminating the cluster so all load balancers get cleaned up correctly.
|
||||
|
||||
```console
|
||||
$ kubectl delete service nginx
|
||||
$ kubectl delete ingress nginx
|
||||
```bash
|
||||
kubectl delete service nginx
|
||||
kubectl delete ingress nginx
|
||||
```
|
||||
|
||||
Give ExternalDNS some time to clean up the DNS records for you. Then delete the managed zone and cluster.
|
||||
|
||||
```console
|
||||
$ gcloud dns managed-zones delete "external-dns-test-gcp-zalan-do"
|
||||
$ gcloud container clusters delete "external-dns"
|
||||
```
|
||||
|
||||
Also delete the NS records for your removed zone from the parent zone.
|
||||
|
||||
```console
|
||||
$ gcloud dns record-sets transaction start --zone "gcp-zalan-do"
|
||||
$ gcloud dns record-sets transaction remove ns-cloud-e{1..4}.googledomains.com. \
|
||||
--name "external-dns-test.gcp.zalan.do." --ttl 300 --type NS --zone "gcp-zalan-do"
|
||||
$ gcloud dns record-sets transaction execute --zone "gcp-zalan-do"
|
||||
```
|
||||
|
||||
## GKE with Workload Identity
|
||||
|
||||
The following instructions use [GKE workload
|
||||
identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity)
|
||||
to provide ExternalDNS with the permissions it needs to manage DNS records.
|
||||
Workload identity is the Google-recommended way to provide GKE workloads access
|
||||
to GCP APIs.
|
||||
|
||||
Create a GKE cluster with workload identity enabled.
|
||||
|
||||
```console
|
||||
$ gcloud container clusters create external-dns \
|
||||
--workload-metadata-from-node=GKE_METADATA_SERVER \
|
||||
--identity-namespace=zalando-external-dns-test.svc.id.goog
|
||||
```
|
||||
|
||||
Create a GCP service account (GSA) for ExternalDNS and save its email address.
|
||||
|
||||
```console
|
||||
$ sa_name="Kubernetes external-dns"
|
||||
$ gcloud iam service-accounts create sa-edns --display-name="$sa_name"
|
||||
$ sa_email=$(gcloud iam service-accounts list --format='value(email)' \
|
||||
--filter="displayName:$sa_name")
|
||||
```
|
||||
|
||||
Bind the ExternalDNS GSA to the DNS admin role.
|
||||
|
||||
```console
|
||||
$ gcloud projects add-iam-policy-binding zalando-external-dns-test \
|
||||
--member="serviceAccount:$sa_email" --role=roles/dns.admin
|
||||
```
|
||||
|
||||
Link the ExternalDNS GSA to the Kubernetes service account (KSA) that
|
||||
external-dns will run under, i.e., the external-dns KSA in the external-dns
|
||||
namespaces.
|
||||
|
||||
```console
|
||||
$ gcloud iam service-accounts add-iam-policy-binding "$sa_email" \
|
||||
--member="serviceAccount:zalando-external-dns-test.svc.id.goog[external-dns/external-dns]" \
|
||||
--role=roles/iam.workloadIdentityUser
|
||||
```
|
||||
|
||||
Create a DNS zone which will contain the managed DNS records.
|
||||
|
||||
```console
|
||||
$ gcloud dns managed-zones create external-dns-test-gcp-zalan-do \
|
||||
--dns-name=external-dns-test.gcp.zalan.do. \
|
||||
--description="Automatically managed zone by ExternalDNS"
|
||||
```
|
||||
|
||||
Make a note of the nameservers that were assigned to your new zone.
|
||||
|
||||
```console
|
||||
$ gcloud dns record-sets list \
|
||||
--zone=external-dns-test-gcp-zalan-do \
|
||||
--name=external-dns-test.gcp.zalan.do. \
|
||||
--type NS
|
||||
NAME TYPE TTL DATA
|
||||
external-dns-test.gcp.zalan.do. NS 21600 ns-cloud-e1.googledomains.com.,ns-cloud-e2.googledomains.com.,ns-cloud-e3.googledomains.com.,ns-cloud-e4.googledomains.com.
|
||||
```
|
||||
|
||||
In this case it's `ns-cloud-{e1-e4}.googledomains.com.` but your's could
|
||||
slightly differ, e.g. `{a1-a4}`, `{b1-b4}` etc.
|
||||
|
||||
Tell the parent zone where to find the DNS records for this zone by adding the
|
||||
corresponding NS records there. Assuming the parent zone is "gcp-zalan-do" and
|
||||
the domain is "gcp.zalan.do" and that it's also hosted at Google we would do the
|
||||
following.
|
||||
|
||||
```console
|
||||
$ gcloud dns record-sets transaction start --zone=gcp-zalan-do
|
||||
$ gcloud dns record-sets transaction add ns-cloud-e{1..4}.googledomains.com. \
|
||||
--name=external-dns-test.gcp.zalan.do. --ttl 300 --type NS --zone=gcp-zalan-do
|
||||
$ gcloud dns record-sets transaction execute --zone=gcp-zalan-do
|
||||
```
|
||||
|
||||
Connect your `kubectl` client to the cluster you just created and bind your GCP
|
||||
user to the cluster admin role in Kubernetes.
|
||||
|
||||
```console
|
||||
$ gcloud container clusters get-credentials external-dns
|
||||
$ kubectl create clusterrolebinding cluster-admin-me \
|
||||
--clusterrole=cluster-admin --user="$(gcloud config get-value account)"
|
||||
```
|
||||
|
||||
### Deploy ExternalDNS
|
||||
|
||||
Apply the following manifest file to deploy external-dns.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
namespace: external-dns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["services", "endpoints", "pods"]
|
||||
verbs: ["get", "watch", "list"]
|
||||
- apiGroups: ["extensions", "networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get", "watch", "list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: external-dns-viewer
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: external-dns
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
namespace: external-dns
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
namespace: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --source=ingress
|
||||
- --source=service
|
||||
- --domain-filter=external-dns-test.gcp.zalan.do
|
||||
- --provider=google
|
||||
- --google-project=zalando-external-dns-test
|
||||
- --registry=txt
|
||||
- --txt-owner-id=my-identifier
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.8.0
|
||||
name: external-dns
|
||||
securityContext:
|
||||
fsGroup: 65534
|
||||
runAsUser: 65534
|
||||
serviceAccountName: external-dns
|
||||
```
|
||||
|
||||
Then add the proper workload identity annotation to the cert-manager service
|
||||
account.
|
||||
|
||||
```bash
|
||||
$ kubectl annotate serviceaccount --namespace=external-dns external-dns \
|
||||
"iam.gke.io/gcp-service-account=$sa_email"
|
||||
gcloud dns managed-zones delete "example-com"
|
||||
gcloud container clusters delete "external-dns"
|
||||
```
|
||||
|
||||
### Deploy a sample application
|
||||
|
||||
Create the following sample application to test that ExternalDNS works.
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
rules:
|
||||
- host: via-ingress.external-dns-test.gcp.zalan.do
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: nginx
|
||||
servicePort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
annotations:
|
||||
external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.gcp.zalan.do.
|
||||
name: nginx
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: nginx
|
||||
type: LoadBalancer
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
```
|
||||
|
||||
After roughly two minutes check that a corresponding DNS records for your
|
||||
service and ingress were created.
|
||||
|
||||
```console
|
||||
$ gcloud dns record-sets list \
|
||||
--zone "external-dns-test-gcp-zalan-do" \
|
||||
--name "via-ingress.external-dns-test.gcp.zalan.do." \
|
||||
--type A
|
||||
NAME TYPE TTL DATA
|
||||
nginx.external-dns-test.gcp.zalan.do. A 300 104.155.60.49
|
||||
nginx.external-dns-test.gcp.zalan.do. TXT 300 "heritage=external-dns,external-dns/owner=my-identifier"
|
||||
via-ingress.external-dns-test.gcp.zalan.do. TXT 300 "heritage=external-dns,external-dns/owner=my-identifier"
|
||||
via-ingress.external-dns-test.gcp.zalan.do. A 300 35.187.1.246
|
||||
```
|
||||
|
||||
Let's check that we can resolve this DNS name as well.
|
||||
|
||||
```console
|
||||
$ dig +short @ns-cloud-e1.googledomains.com. via-ingress.external-dns-test.gcp.zalan.do.
|
||||
35.187.1.246
|
||||
```
|
||||
|
||||
Try with `curl` as well.
|
||||
|
||||
```console
|
||||
$ curl via-ingress.external-dns-test.gcp.zalan.do
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Welcome to nginx!</title>
|
||||
...
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Clean up
|
||||
|
||||
Make sure to delete all service and ingress objects before terminating the
|
||||
cluster so all load balancers and DNS entries get cleaned up correctly.
|
||||
|
||||
```console
|
||||
$ kubectl delete ingress nginx
|
||||
$ kubectl delete service nginx
|
||||
```
|
||||
|
||||
Give ExternalDNS some time to clean up the DNS records for you. Then delete the
|
||||
managed zone and cluster.
|
||||
|
||||
```console
|
||||
$ gcloud dns managed-zones delete external-dns-test-gcp-zalan-do
|
||||
$ gcloud container clusters delete external-dns
|
||||
```
|
||||
|
||||
Also delete the NS records for your removed zone from the parent zone.
|
||||
|
||||
```console
|
||||
$ gcloud dns record-sets transaction start --zone gcp-zalan-do
|
||||
$ gcloud dns record-sets transaction remove ns-cloud-e{1..4}.googledomains.com. \
|
||||
--name=external-dns-test.gcp.zalan.do. --ttl 300 --type NS --zone=gcp-zalan-do
|
||||
$ gcloud dns record-sets transaction execute --zone=gcp-zalan-do
|
||||
```
|
||||
|
||||
## User Demo How-To Blogs and Examples
|
||||
|
||||
* A full demo on GKE Kubernetes + CloudDNS + SA-Permissions [How-to Kubernetes with DNS management (ssl-manager pre-req)](https://medium.com/@jpantjsoha/how-to-kubernetes-with-dns-management-for-gitops-31239ea75d8d)
|
||||
* Run external-dns on GKE with workload identity. See [Kubernetes, ingress-nginx, cert-manager & external-dns](https://blog.atomist.com/kubernetes-ingress-nginx-cert-manager-external-dns/)
|
||||
|
@ -22,7 +22,7 @@ spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
# update this to the desired external-dns version
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=gloo-proxy
|
||||
- --gloo-namespace=custom-gloo-system # gloo system namespace. Omit to use the default (gloo-system)
|
||||
@ -90,7 +90,7 @@ spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
# update this to the desired external-dns version
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=gloo-proxy
|
||||
- --gloo-namespace=custom-gloo-system # gloo system namespace. Omit to use the default (gloo-system)
|
||||
|
@ -44,7 +44,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.7
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -115,7 +115,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.7
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -31,7 +31,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --log-level=debug
|
||||
- --source=service
|
||||
@ -96,7 +96,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --log-level=debug
|
||||
- --source=service
|
||||
@ -198,3 +198,34 @@ kafka-1.ksvc.example.org
|
||||
kafka-2.ksvc.example.org
|
||||
```
|
||||
|
||||
#### Using pods' HostIPs as targets
|
||||
|
||||
Add the following annotation to your `Service`:
|
||||
|
||||
```yaml
|
||||
external-dns.alpha.kubernetes.io/endpoints-type: HostIP
|
||||
```
|
||||
|
||||
external-dns will now publish the value of the `.status.hostIP` field of the pods backing your `Service`.
|
||||
|
||||
#### Using node external IPs as targets
|
||||
|
||||
Add the following annotation to your `Service`:
|
||||
|
||||
```yaml
|
||||
external-dns.alpha.kubernetes.io/endpoints-type: NodeExternalIP
|
||||
```
|
||||
|
||||
external-dns will now publish the node external IP (`.status.addresses` entries of with `type: NodeExternalIP`) of the nodes on which the pods backing your `Service` are running.
|
||||
|
||||
#### Using pod annotations to specify target IPs
|
||||
|
||||
Add the following annotation to the **pods** backing your `Service`:
|
||||
|
||||
```yaml
|
||||
external-dns.alpha.kubernetes.io/target: "1.2.3.4"
|
||||
```
|
||||
|
||||
external-dns will publish the IP specified in the annotation of each pod instead of using the podIP advertised by Kubernetes.
|
||||
|
||||
This can be useful e.g. if you are NATing public IPs onto your pod IPs and want to publish these in DNS.
|
||||
|
262
docs/tutorials/ibmcloud.md
Normal file
262
docs/tutorials/ibmcloud.md
Normal file
@ -0,0 +1,262 @@
|
||||
# Setting up ExternalDNS for Services on IBMCloud
|
||||
|
||||
This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using IBMCloud DNS.
|
||||
|
||||
This tutorial uses [IBMCloud CLI](https://cloud.ibm.com/docs/cli?topic=cli-getting-started) for all
|
||||
IBM Cloud commands and assumes that the Kubernetes cluster was created via IBM Cloud Kubernetes Service and `kubectl` commands
|
||||
are being run on an orchestration node.
|
||||
|
||||
## Creating a IBMCloud DNS zone
|
||||
The IBMCloud provider for ExternalDNS will find suitable zones for domains it manages; it will
|
||||
not automatically create zones.
|
||||
For public zone, This tutorial assume that the [IBMCloud Internet Services](https://cloud.ibm.com/catalog/services/internet-services) was provisioned and the [cis cli plugin](https://cloud.ibm.com/docs/cis?topic=cis-cli-plugin-cis-cli) was installed with IBMCloud CLI
|
||||
For private zone, This tutorial assume that the [IBMCloud DNS Services](https://cloud.ibm.com/catalog/services/dns-services) was provisioned and the [dns cli plugin](https://cloud.ibm.com/docs/dns-svcs?topic=dns-svcs-cli-plugin-dns-services-cli-commands) was installed with IBMCloud CLI
|
||||
|
||||
### Public Zone
|
||||
For this tutorial, we create public zone named `example.com` on IBMCloud Internet Services instance `external-dns-public`
|
||||
```
|
||||
$ ibmcloud cis domain-add example.com -i external-dns-public
|
||||
```
|
||||
Follow [step](https://cloud.ibm.com/docs/cis?topic=cis-getting-started#configure-your-name-servers-with-the-registrar-or-existing-dns-provider) to active your zone
|
||||
|
||||
### Private Zone
|
||||
For this tutorial, we create private zone named `example.com` on IBMCloud DNS Services instance `external-dns-private`
|
||||
```
|
||||
$ ibmcloud dns zone-create example.com -i external-dns-private
|
||||
```
|
||||
|
||||
## Creating configuration file
|
||||
|
||||
The preferred way to inject the configuration file is by using a Kubernetes secret. The secret should contain an object named azure.json with content similar to this:
|
||||
|
||||
```
|
||||
{
|
||||
"apiKey": "1234567890abcdefghijklmnopqrstuvwxyz",
|
||||
"instanceCrn": "crn:v1:bluemix:public:internet-svcs:global:a/bcf1865e99742d38d2d5fc3fb80a5496:b950da8a-5be6-4691-810e-36388c77b0a3::"
|
||||
}
|
||||
```
|
||||
|
||||
You can create or find the `apiKey` in your ibmcloud IAM --> [API Keys page](https://cloud.ibm.com/iam/apikeys)
|
||||
|
||||
You can find the `instanceCrn` in your service instance details
|
||||
|
||||
Now you can create a file named 'ibmcloud.json' with values gathered above and with the structure of the example above. Use this file to create a Kubernetes secret:
|
||||
```
|
||||
$ kubectl create secret generic ibmcloud-config-file --from-file=/local/path/to/ibmcloud.json
|
||||
```
|
||||
## Deploy ExternalDNS
|
||||
|
||||
Connect your `kubectl` client to the cluster you want to test ExternalDNS with.
|
||||
Then apply one of the following manifests file to deploy ExternalDNS.
|
||||
|
||||
### Manifest (for clusters without RBAC enabled)
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
- --provider=ibmcloud
|
||||
- --ibmcloud-proxied # (optional) enable the proxy feature of IBMCloud
|
||||
volumeMounts:
|
||||
- name: ibmcloud-config-file
|
||||
mountPath: /etc/kubernetes
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: ibmcloud-config-file
|
||||
secret:
|
||||
secretName: ibmcloud-config-file
|
||||
items:
|
||||
- key: externaldns-config.json
|
||||
path: ibmcloud.json
|
||||
```
|
||||
|
||||
### Manifest (for clusters with RBAC enabled)
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["services","endpoints","pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions","networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list", "watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: external-dns-viewer
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: external-dns
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
- --provider=ibmcloud
|
||||
- --ibmcloud-proxied # (optional) enable the proxy feature of IBMCloud public zone
|
||||
volumeMounts:
|
||||
- name: ibmcloud-config-file
|
||||
mountPath: /etc/kubernetes
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: ibmcloud-config-file
|
||||
secret:
|
||||
secretName: ibmcloud-config-file
|
||||
items:
|
||||
- key: externaldns-config.json
|
||||
path: ibmcloud.json
|
||||
```
|
||||
|
||||
## Deploying an Nginx Service
|
||||
|
||||
Create a service file called `nginx.yaml` with the following contents:
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
external-dns.alpha.kubernetes.io/hostname: www.example.com
|
||||
external-dns.alpha.kubernetes.io/ttl: "120" #optional
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
```
|
||||
|
||||
Note the annotation on the service; use the hostname as the IBMCloud DNS zone created above. The annotation may also be a subdomain
|
||||
of the DNS zone (e.g. 'www.example.com').
|
||||
|
||||
By setting the TTL annotation on the service, you have to pass a valid TTL, which must be 120 or above.
|
||||
This annotation is optional, if you won't set it, it will be 1 (automatic) which is 300.
|
||||
|
||||
ExternalDNS uses this annotation to determine what services should be registered with DNS. Removing the annotation
|
||||
will cause ExternalDNS to remove the corresponding DNS records.
|
||||
|
||||
Create the deployment and service:
|
||||
|
||||
```
|
||||
$ kubectl create -f nginx.yaml
|
||||
```
|
||||
|
||||
Depending where you run your service it can take a little while for your cloud provider to create an external IP for the service.
|
||||
|
||||
Once the service has an external IP assigned, ExternalDNS will notice the new service IP address and synchronize
|
||||
the IBMCloud DNS records.
|
||||
|
||||
## Verifying IBMCloud DNS records
|
||||
Run the following command to view the A records:
|
||||
|
||||
### Public Zone
|
||||
```
|
||||
# Get the domain ID with below command on IBMCloud Internet Services instance `external-dns-public`
|
||||
$ ibmcloud cis domains -i external-dns-public
|
||||
# Get the records with domain ID
|
||||
$ ibmcloud cis dns-records DOMAIN_ID -i external-dns-public
|
||||
```
|
||||
|
||||
### Private Zone
|
||||
```
|
||||
# Get the domain ID with below command on IBMCloud DNS Services instance `external-dns-private`
|
||||
$ ibmcloud dns zones -i external-dns-private
|
||||
# Get the records with domain ID
|
||||
$ ibmcloud dns resource-records ZONE_ID -i external-dns-public
|
||||
```
|
||||
This should show the external IP address of the service as the A record for your domain.
|
||||
|
||||
## Cleanup
|
||||
|
||||
Now that we have verified that ExternalDNS will automatically manage IBMCloud DNS records, we can delete the tutorial's example:
|
||||
|
||||
```
|
||||
$ kubectl delete -f nginx.yaml
|
||||
$ kubectl delete -f externaldns.yaml
|
||||
```
|
||||
|
||||
## Setting proxied records on public zone
|
||||
|
||||
Using the `external-dns.alpha.kubernetes.io/ibmcloud-proxied: "true"` annotation on your ingress or service, you can specify if the proxy feature of IBMCloud public DNS should be enabled for that record. This setting will override the global `--ibmcloud-proxied` setting.
|
||||
|
||||
## Active priviate zone with VPC allocated
|
||||
|
||||
By default, IBMCloud DNS Services don't active your private zone with new zone added, with externale DNS, you can use `external-dns.alpha.kubernetes.io/ibmcloud-vpc: "crn:v1:bluemix:public:is:us-south:a/bcf1865e99742d38d2d5fc3fb80a5496::vpc:r006-74353823-a60d-42e4-97c5-5e2551278435"` annotation on your ingress or service, it will active your private zone with in specific VPC for that record created in. this setting won't work if the private zone was active already.
|
||||
|
||||
Note: the annotaion value is the VPC CRN, every IBM Cloud service have a valid CRN.
|
@ -69,7 +69,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains.
|
||||
@ -150,7 +150,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains.
|
||||
@ -271,6 +271,14 @@ There is also the ability to filter results from the Infoblox zone_auth service
|
||||
--infoblox-fqdn-regex=^staging.*test.com$
|
||||
```
|
||||
|
||||
## Ability to filter A, Host, CNAME and TXT records from the by name using a regular expression
|
||||
|
||||
Infoblox supports filtering records by name using a regular expression. See the [Infoblox API document](https://www.infoblox.com/wp-content/uploads/infoblox-deployment-infoblox-rest-api.pdf) for examples. To use this feature, set the parameter infoblox-name-regex for external-dns to a regular expression that makes sense for you. For instance, if all your dns records end with `cluster1.example.com`, you can fetch records matching this style by setting the following:
|
||||
|
||||
```
|
||||
--infoblox-name-regex=cluster1.example.com
|
||||
```
|
||||
|
||||
## Infoblox PTR record support
|
||||
|
||||
There is an option to enable PTR records support for infoblox provider. PTR records allow to do reverse dns search. To enable PTR records support, add following into arguments for external-dns:
|
||||
|
@ -28,7 +28,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -98,7 +98,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
|
@ -22,7 +22,7 @@ spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
# update this to the desired external-dns version
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.9.0
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=kong-tcpingress
|
||||
- --provider=aws
|
||||
@ -86,7 +86,7 @@ spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
# update this to the desired external-dns version
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.9.0
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=kong-tcpingress
|
||||
- --provider=aws
|
||||
|
@ -41,7 +41,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -105,7 +105,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -273,7 +273,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=ingress
|
||||
- --domain-filter=external-dns-test.gcp.zalan.do
|
||||
@ -570,7 +570,7 @@ spec:
|
||||
- --google-project=zalando-external-dns-test
|
||||
- --registry=txt
|
||||
- --txt-owner-id=my-identifier
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
name: external-dns
|
||||
securityContext:
|
||||
fsGroup: 65534
|
||||
|
114
docs/tutorials/nodes.md
Normal file
114
docs/tutorials/nodes.md
Normal file
@ -0,0 +1,114 @@
|
||||
# Configuring ExternalDNS to use Cluster Nodes as Source
|
||||
|
||||
This tutorial describes how to configure ExternalDNS to use the cluster nodes as source.
|
||||
Using nodes (`--source=node`) as source is possible to synchronize a DNS zone with the nodes of a cluster.
|
||||
|
||||
The node source adds an `A` record per each node `externalIP` (if not found, node's `internalIP` is used).
|
||||
The TTL record can be set with the `external-dns.alpha.kubernetes.io/ttl` node annotation.
|
||||
|
||||
## Manifest (for cluster without RBAC enabled)
|
||||
|
||||
```
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=node # will use nodes as source
|
||||
- --provider=aws
|
||||
- --zone-name-filter=external-dns-test.my-org.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
|
||||
- --domain-filter=external-dns-test.my-org.com
|
||||
- --aws-zone-type=public
|
||||
- --registry=txt
|
||||
- --fqdn-template={{.Name}}.external-dns-test.my-org.com
|
||||
- --txt-owner-id=my-identifier
|
||||
- --policy=sync
|
||||
- --log-level=debug
|
||||
```
|
||||
|
||||
## Manifest (for cluster with RBAC enabled)
|
||||
|
||||
```
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
rules:
|
||||
- apiGroups: ["route.openshift.io"]
|
||||
resources: ["routes"]
|
||||
verbs: ["get", "watch", "list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["services","endpoints","pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions","networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["get", "watch", "list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: external-dns-viewer
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: external-dns
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
namespace: external-dns
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=node # will use nodes as source
|
||||
- --provider=aws
|
||||
- --zone-name-filter=external-dns-test.my-org.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
|
||||
- --domain-filter=external-dns-test.my-org.com
|
||||
- --aws-zone-type=public
|
||||
- --registry=txt
|
||||
- --fqdn-template={{.Name}}.external-dns-test.my-org.com
|
||||
- --txt-owner-id=my-identifier
|
||||
- --policy=sync
|
||||
- --log-level=debug
|
||||
```
|
@ -61,7 +61,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -125,7 +125,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -4,46 +4,34 @@ It is meant to supplement the other provider-specific setup tutorials.
|
||||
|
||||
### For OCP 4.x
|
||||
|
||||
In OCP 4.x, if you have multiple ingress controllers then you must specify an ingress controller name or a router name(you can get it from the route's Status.Ingress.RouterName field).
|
||||
If you don't specify an ingress controller's or router name when you have multiple ingresscontrollers in your environment then the route gets populated with multiple entries of router canonical hostnames which causes external dns to create a CNAME record with multiple router canonical hostnames pointing to the route host which is a violation of RFC 1912 and is not allowed by Cloud Providers which leads to failure of record creation.
|
||||
Once you specify the ingresscontroller or router name then that will be matched by the external-dns and the router canonical hostname corresponding to this routerName(which is present in route's Status.Ingress.RouterName field) is selected and a CNAME record of this route host pointing to this router canonical hostname is created.
|
||||
|
||||
Your externaldns CR shall be created as per the following example.
|
||||
Replace names in the domain section and zone ID as per your environment.
|
||||
This is example is for AWS environment.
|
||||
In OCP 4.x, if you have multiple [OpenShift ingress controllers](https://docs.openshift.com/container-platform/4.9/networking/ingress-operator.html) then you must specify an ingress controller name (also called router name), you can get it from the route's `status.ingress[*].routerName` field.
|
||||
If you don't specify a router name when you have multiple ingress controllers in your cluster then the first router from the route's `status.ingress` will be used. Note that the router must have admitted the route in order to be selected.
|
||||
Once the router is known, ExternalDNS will use this router's canonical hostname as the target for the CNAME record.
|
||||
|
||||
Starting from OCP 4.10 you can use [ExternalDNS Operator](https://github.com/openshift/external-dns-operator) to manage ExternalDNS instances. Example of its custom resource for AWS provider:
|
||||
```yaml
|
||||
|
||||
apiVersion: externaldns.olm.openshift.io/v1alpha1
|
||||
kind: ExternalDNS
|
||||
metadata:
|
||||
name: sample1
|
||||
name: sample
|
||||
spec:
|
||||
domains:
|
||||
- filterType: Include
|
||||
matchType: Exact
|
||||
names: apps.miheer.externaldns
|
||||
provider:
|
||||
type: AWS
|
||||
source:
|
||||
hostnameAnnotation: Allow
|
||||
openshiftRouteOptions:
|
||||
routerName: default
|
||||
type: OpenShiftRoute
|
||||
zones:
|
||||
- Z05387772BD5723IZFRX3
|
||||
|
||||
```
|
||||
|
||||
This will create an externaldns pod with the following container args under spec in the external-dns namespace where `- --source=openshift-route` and `- --openshift-router-name=default` is added by the external-dns-operator.
|
||||
|
||||
This will create an ExternalDNS POD with the following container args in `external-dns` namespace:
|
||||
```
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --domain-filter=apps.misalunk.externaldns
|
||||
- --metrics-address=127.0.0.1:7979
|
||||
- --txt-owner-id=external-dns-sample1
|
||||
- --txt-owner-id=external-dns-sample
|
||||
- --provider=aws
|
||||
- --source=openshift-route
|
||||
- --policy=sync
|
||||
@ -52,7 +40,6 @@ spec:
|
||||
- --zone-id-filter=Z05387772BD5723IZFRX3
|
||||
- --openshift-router-name=default
|
||||
- --txt-prefix=external-dns-
|
||||
|
||||
```
|
||||
|
||||
### For OCP 3.11 environment
|
||||
@ -79,7 +66,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=openshift-route
|
||||
- --domain-filter=external-dns-test.my-org.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
|
||||
@ -146,7 +133,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=openshift-route
|
||||
- --domain-filter=external-dns-test.my-org.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
|
||||
|
@ -6,16 +6,25 @@ Make sure to use the latest version of ExternalDNS for this tutorial.
|
||||
|
||||
## Creating an OCI DNS Zone
|
||||
|
||||
Create a DNS zone which will contain the managed DNS records. Let's use `example.com` as an reference here.
|
||||
Create a DNS zone which will contain the managed DNS records. Let's use
|
||||
`example.com` as a reference here. Make note of the OCID of the compartment
|
||||
in which you created the zone; you'll need to provide that later.
|
||||
|
||||
For more information about OCI DNS see the documentation [here][1].
|
||||
|
||||
## Deploy ExternalDNS
|
||||
|
||||
Connect your `kubectl` client to the cluster you want to test ExternalDNS with.
|
||||
The OCI provider supports two authentication options: key-based and instance
|
||||
principals.
|
||||
|
||||
### Key-based
|
||||
|
||||
We first need to create a config file containing the information needed to connect with the OCI API.
|
||||
|
||||
Create a new file (oci.yaml) and modify the contents to match the example below. Be sure to adjust the values to match your own credentials:
|
||||
Create a new file (oci.yaml) and modify the contents to match the example
|
||||
below. Be sure to adjust the values to match your own credentials, and the OCID
|
||||
of the compartment containing the zone:
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
@ -37,7 +46,29 @@ Create a secret using the config file above:
|
||||
$ kubectl create secret generic external-dns-config --from-file=oci.yaml
|
||||
```
|
||||
|
||||
### Manifest (for clusters with RBAC enabled)
|
||||
### OCI IAM Instance Principal
|
||||
|
||||
If you're running ExternalDNS within OCI, you can use OCI IAM instance
|
||||
principals to authenticate with OCI. This obviates the need to create the
|
||||
secret with your credentials. You'll need to ensure an OCI IAM policy exists
|
||||
with a statement granting the `manage dns` permission on zones and records in
|
||||
the target compartment to the dynamic group covering your instance running
|
||||
ExternalDNS.
|
||||
E.g.:
|
||||
|
||||
```
|
||||
Allow dynamic-group <dynamic-group-name> to manage dns in compartment id <target-compartment-OCID>
|
||||
```
|
||||
|
||||
You'll also need to add the `--oci-auth-instance-principal` flag to enable
|
||||
this type of authentication. Finally, you'll need to add the
|
||||
`--oci-compartment-ocid=ocid1.compartment.oc1...` flag to provide the OCID of
|
||||
the compartment containing the zone to be managed.
|
||||
|
||||
For more information about OCI IAM instance principals, see the documentation [here][2].
|
||||
For more information about OCI IAM policy details for the DNS service, see the documentation [here][3].
|
||||
|
||||
## Manifest (for clusters with RBAC enabled)
|
||||
|
||||
Apply the following manifest to deploy ExternalDNS.
|
||||
|
||||
@ -93,7 +124,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -159,3 +190,6 @@ $ kubectl apply -f nginx.yaml
|
||||
```
|
||||
|
||||
[1]: https://docs.cloud.oracle.com/iaas/Content/DNS/Concepts/dnszonemanagement.htm
|
||||
[2]: https://docs.cloud.oracle.com/iaas/Content/Identity/Reference/dnspolicyreference.htm
|
||||
[3]: https://docs.cloud.oracle.com/iaas/Content/Identity/Tasks/callingservicesfrominstances.htm
|
||||
|
||||
|
@ -86,7 +86,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -160,7 +160,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -42,7 +42,7 @@ spec:
|
||||
# serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # or ingress or both
|
||||
- --provider=pdns
|
||||
|
188
docs/tutorials/pihole.md
Normal file
188
docs/tutorials/pihole.md
Normal file
@ -0,0 +1,188 @@
|
||||
# Setting up ExternalDNS for Pi-hole
|
||||
|
||||
This tutorial describes how to setup ExternalDNS to sync records with Pi-hole's Custom DNS.
|
||||
Pi-hole has an internal list it checks last when resolving requests. This list can contain any number of arbitrary A or CNAME records.
|
||||
There is a pseudo-API exposed that ExternalDNS is able to use to manage these records.
|
||||
|
||||
## Deploy ExternalDNS
|
||||
|
||||
You can skip to the [manifest](#externaldns-manifest) if authentication is disabled on your Pi-hole instance or you don't want to use secrets.
|
||||
|
||||
If your Pi-hole server's admin dashboard is protected by a password, you'll likely want to create a secret first containing its value.
|
||||
This is optional since you _do_ retain the option to pass it as a flag with `--pihole-password`.
|
||||
|
||||
You can create the secret with:
|
||||
|
||||
```bash
|
||||
kubectl create secret generic pihole-password \
|
||||
--from-literal EXTERNAL_DNS_PIHOLE_PASSWORD=supersecret
|
||||
```
|
||||
|
||||
Replacing **"supersecret"** with the actual password to your Pi-hole server.
|
||||
|
||||
### ExternalDNS Manifest
|
||||
|
||||
Apply the following manifest to deploy ExternalDNS, editing values for your environment accordingly.
|
||||
Be sure to change the namespace in the `ClusterRoleBinding` if you are using a namespace other than **default**.
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["services","endpoints","pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions","networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list","watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: external-dns-viewer
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: external-dns
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
# If authentication is disabled and/or you didn't create
|
||||
# a secret, you can remove this block.
|
||||
envFrom:
|
||||
- secretRef:
|
||||
# Change this if you gave the secret a different name
|
||||
name: pihole-password
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
# Pihole only supports A/CNAME records so there is no mechanism to track ownership.
|
||||
# You don't need to set this flag, but if you leave it unset, you will receive warning
|
||||
# logs when ExternalDNS attempts to create TXT records.
|
||||
- --registry=noop
|
||||
# IMPORTANT: If you have records that you manage manually in Pi-hole, set
|
||||
# the policy to upsert-only so they do not get deleted.
|
||||
- --policy=upsert-only
|
||||
- --provider=pihole
|
||||
# Change this to the actual address of your Pi-hole web server
|
||||
- --pihole-server=http://pihole-web.pihole.svc.cluster.local
|
||||
securityContext:
|
||||
fsGroup: 65534 # For ExternalDNS to be able to read Kubernetes token files
|
||||
```
|
||||
|
||||
### Arguments
|
||||
|
||||
- `--pihole-server (env: EXTERNAL_DNS_PIHOLE_SERVER)` - The address of the Pi-hole web server
|
||||
- `--pihole-password (env: EXTERNAL_DNS_PIHOLE_PASSWORD)` - The password to the Pi-hole web server (if enabled)
|
||||
- `--pihole-tls-skip-verify (env: EXTERNAL_DNS_PIHOLE_TLS_SKIP_VERIFY)` - Skip verification of any TLS certificates served by the Pi-hole web server.
|
||||
|
||||
## Verify ExternalDNS Works
|
||||
|
||||
### Ingress Example
|
||||
|
||||
Create an Ingress resource. ExternalDNS will use the hostname specified in the Ingress object.
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: foo
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: foo.bar.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: foo
|
||||
port:
|
||||
number: 80
|
||||
```
|
||||
|
||||
### Service Example
|
||||
|
||||
The below sample application can be used to verify Services work.
|
||||
For services ExternalDNS will look for the annotation `external-dns.alpha.kubernetes.io/hostname` on the service and use the corresponding value.
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.homelab.com
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- port: 80
|
||||
name: http
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: nginx
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: http
|
||||
```
|
||||
|
||||
You can then query your Pi-hole to see if the record was created.
|
||||
|
||||
_Change `@192.168.100.2` to the actual address of your DNS server_
|
||||
|
||||
```bash
|
||||
$ dig +short @192.168.100.2 nginx.external-dns-test.homelab.com
|
||||
|
||||
192.168.100.129
|
||||
```
|
197
docs/tutorials/plural.md
Normal file
197
docs/tutorials/plural.md
Normal file
@ -0,0 +1,197 @@
|
||||
# Setting up ExternalDNS for Services on Plural
|
||||
|
||||
This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using Plural DNS.
|
||||
|
||||
Make sure to use **>=0.12.3** version of ExternalDNS for this tutorial.
|
||||
|
||||
## Creating Plural Credentials
|
||||
|
||||
A secret containing the a Plural access token is needed for this provider. You can get a token for your user [here](https://app.plural.sh/profile/tokens).
|
||||
|
||||
To create the secret you can run `kubectl create secret generic plural-env --from-literal=PLURAL_ACCESS_TOKEN=<replace-with-your-access-token>`.
|
||||
|
||||
## Deploy ExternalDNS
|
||||
|
||||
Connect your `kubectl` client to the cluster you want to test ExternalDNS with.
|
||||
Then apply one of the following manifests file to deploy ExternalDNS.
|
||||
|
||||
### Manifest (for clusters without RBAC enabled)
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
- --provider=plural
|
||||
- --plural-cluster=example-plural-cluster
|
||||
- --plural-provider=aws # gcp, azure, equinix and kind are also possible
|
||||
env:
|
||||
- name: PLURAL_ACCESS_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
key: PLURAL_ACCESS_TOKEN
|
||||
name: plural-env
|
||||
- name: PLURAL_ENDPOINT # (optional) use an alternative endpoint for Plural; defaults to https://app.plural.sh
|
||||
value: https://app.plural.sh
|
||||
```
|
||||
|
||||
### Manifest (for clusters with RBAC enabled)
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["services","endpoints","pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions","networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list", "watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: external-dns-viewer
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: external-dns
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
- --provider=plural
|
||||
- --plural-cluster=example-plural-cluster
|
||||
- --plural-provider=aws # gcp, azure, equinix and kind are also possible
|
||||
env:
|
||||
- name: PLURAL_ACCESS_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
key: PLURAL_ACCESS_TOKEN
|
||||
name: plural-env
|
||||
- name: PLURAL_ENDPOINT # (optional) use an alternative endpoint for Plural; defaults to https://app.plural.sh
|
||||
value: https://app.plural.sh
|
||||
```
|
||||
|
||||
## Deploying an Nginx Service
|
||||
|
||||
Create a service file called 'nginx.yaml' with the following contents:
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
external-dns.alpha.kubernetes.io/hostname: example.com
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
```
|
||||
|
||||
Note the annotation on the service; use the same hostname as the Plural DNS zone created above. The annotation may also be a subdomain
|
||||
of the DNS zone (e.g. 'www.example.com').
|
||||
|
||||
By setting the TTL annotation on the service, you have to pass a valid TTL, which must be 120 or above.
|
||||
This annotation is optional, if you won't set it, it will be 1 (automatic) which is 300.
|
||||
|
||||
ExternalDNS uses this annotation to determine what services should be registered with DNS. Removing the annotation
|
||||
will cause ExternalDNS to remove the corresponding DNS records.
|
||||
|
||||
Create the deployment and service:
|
||||
|
||||
```
|
||||
$ kubectl create -f nginx.yaml
|
||||
```
|
||||
|
||||
Depending where you run your service it can take a little while for your cloud provider to create an external IP for the service.
|
||||
|
||||
Once the service has an external IP assigned, ExternalDNS will notice the new service IP address and synchronize
|
||||
the Plural DNS records.
|
||||
|
||||
## Verifying Plural DNS records
|
||||
|
||||
Check your [Plural domain overview](https://app.plural.sh/account/domains) to view the domains associated with your Plural account. There you can view the records for each domain.
|
||||
|
||||
The records should show the external IP address of the service as the A record for your domain.
|
||||
|
||||
## Cleanup
|
||||
|
||||
Now that we have verified that ExternalDNS will automatically manage Plural DNS records, we can delete the tutorial's example:
|
||||
|
||||
```
|
||||
$ kubectl delete -f nginx.yaml
|
||||
$ kubectl delete -f externaldns.yaml
|
@ -245,7 +245,7 @@ spec:
|
||||
# ... or, if you use annotations for ingress classes
|
||||
# - --annotation-filter=kubernetes.io/ingress.class in (external-ingress)
|
||||
- --aws-zone-type=public
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
name: external-dns-public
|
||||
```
|
||||
|
||||
@ -285,7 +285,7 @@ spec:
|
||||
# ... or, if you use annotations for ingress classes
|
||||
# - --annotation-filter=kubernetes.io/ingress.class in (internal-ingress)
|
||||
- --aws-zone-type=private
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
name: external-dns-private
|
||||
```
|
||||
|
||||
@ -313,8 +313,11 @@ spec:
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: app
|
||||
servicePort: 80
|
||||
service:
|
||||
name: app
|
||||
port:
|
||||
number: 80
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
Then create private Ingress definition (again, make sure to un-comment either the `annotations` or `ingressClassName` lines):
|
||||
@ -337,8 +340,11 @@ spec:
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: app
|
||||
servicePort: 80
|
||||
service:
|
||||
name: app
|
||||
port:
|
||||
number: 80
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
Additionally, you may leverage [cert-manager](https://github.com/jetstack/cert-manager) to automatically issue SSL certificates from [Let's Encrypt](https://letsencrypt.org/). To do that, request a certificate in public service definition:
|
||||
@ -362,8 +368,11 @@ spec:
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: app
|
||||
servicePort: 80
|
||||
service:
|
||||
name: app
|
||||
port:
|
||||
number: 80
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- hosts:
|
||||
- app.domain.com
|
||||
@ -386,8 +395,11 @@ spec:
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: app
|
||||
servicePort: 80
|
||||
service:
|
||||
name: app
|
||||
port:
|
||||
number: 80
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- hosts:
|
||||
- app.domain.com
|
||||
|
@ -53,7 +53,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -120,7 +120,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -54,7 +54,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=ingress
|
||||
- --provider=rdns
|
||||
@ -123,7 +123,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=ingress
|
||||
- --provider=rdns
|
||||
|
@ -218,7 +218,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --registry=txt
|
||||
- --txt-prefix=external-dns-
|
||||
@ -260,7 +260,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --registry=txt
|
||||
- --txt-prefix=external-dns-
|
||||
|
@ -24,6 +24,7 @@ Note that you will also need to the Organization ID, which can be retrieve on th
|
||||
Three environment variables are needed to run ExternalDNS with Scaleway DNS:
|
||||
- `SCW_ACCESS_KEY` which is the Access Key.
|
||||
- `SCW_SECRET_KEY` which is the Secret Key.
|
||||
- `SCW_DEFAULT_ORGANIZATION_ID` which is the Default Organization ID.
|
||||
|
||||
## Deploy ExternalDNS
|
||||
|
||||
@ -52,7 +53,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.8.0
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -62,6 +63,8 @@ spec:
|
||||
value: "<your access key>"
|
||||
- name: SCW_SECRET_KEY
|
||||
value: "<your secret key>"
|
||||
- name: SCW_DEFAULT_ORGANIZATION_ID
|
||||
value: "<your default organization ID>"
|
||||
```
|
||||
|
||||
### Manifest (for clusters with RBAC enabled)
|
||||
@ -118,7 +121,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.8.0
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -128,6 +131,8 @@ spec:
|
||||
value: "<your access key>"
|
||||
- name: SCW_SECRET_KEY
|
||||
value: "<your secret key>"
|
||||
- name: SCW_DEFAULT_ORGANIZATION_ID
|
||||
value: "<your default organization ID>"
|
||||
```
|
||||
|
||||
|
||||
|
@ -20,7 +20,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- ... # your arguments here
|
||||
securityContext:
|
||||
|
214
docs/tutorials/tencentcloud.md
Normal file
214
docs/tutorials/tencentcloud.md
Normal file
@ -0,0 +1,214 @@
|
||||
# Setting up ExternalDNS for Tencent Cloud
|
||||
|
||||
## External Dns Version
|
||||
* Make sure to use **>=0.13.1** version of ExternalDNS for this tutorial
|
||||
|
||||
## Set up PrivateDns or DNSPod
|
||||
|
||||
Tencent Cloud DNSPod Service is the domain name resolution and management service for public access.
|
||||
Tencent Cloud PrivateDNS Service is the domain name resolution and management service for VPC internal access.
|
||||
|
||||
* If you want to use internal dns service in Tencent Cloud.
|
||||
1. Set up the args `--tencent-cloud-zone-type=private`
|
||||
2. Create a DNS domain in PrivateDNS console. DNS domain which will contain the managed DNS records.
|
||||
|
||||
* If you want to use public dns service in Tencent Cloud.
|
||||
1. Set up the args `--tencent-cloud-zone-type=public`
|
||||
2. Create a Domain in DnsPod console. DNS domain which will contain the managed DNS records.
|
||||
|
||||
## Set up CAM for API Key
|
||||
|
||||
In Tencent CAM Console. you may get the secretId and secretKey pair. make sure the key pair has those Policy.
|
||||
```json
|
||||
{
|
||||
"version": "2.0",
|
||||
"statement": [
|
||||
{
|
||||
"effect": "allow",
|
||||
"action": [
|
||||
"dnspod:ModifyRecord",
|
||||
"dnspod:DeleteRecord",
|
||||
"dnspod:CreateRecord",
|
||||
"dnspod:DescribeRecordList",
|
||||
"dnspod:DescribeDomainList"
|
||||
],
|
||||
"resource": [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"effect": "allow",
|
||||
"action": [
|
||||
"privatedns:DescribePrivateZoneList",
|
||||
"privatedns:DescribePrivateZoneRecordList",
|
||||
"privatedns:CreatePrivateZoneRecord",
|
||||
"privatedns:DeletePrivateZoneRecord",
|
||||
"privatedns:ModifyPrivateZoneRecord"
|
||||
],
|
||||
"resource": [
|
||||
"*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
# Deploy ExternalDNS
|
||||
|
||||
## Manifest (for clusters with RBAC enabled)
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["services","endpoints","pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions","networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: external-dns-viewer
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: external-dns
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: external-dns
|
||||
data:
|
||||
tencent-cloud.json: |
|
||||
{
|
||||
"regionId": "ap-shanghai",
|
||||
"secretId": "******",
|
||||
"secretKey": "******",
|
||||
"vpcId": "vpc-******",
|
||||
"internetEndpoint": false # Default: false. Access the Tencent API through the intranet. If you need to deploy on the public network, you need to change to true
|
||||
}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --domain-filter=external-dns-test.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
|
||||
- --provider=tencentcloud
|
||||
- --policy=sync # set `upsert-only` would prevent ExternalDNS from deleting any records
|
||||
- --tencent-cloud-zone-type=private # only look at private hosted zones. set `public` to use the public dns service.
|
||||
- --tencent-cloud-config-file=/etc/kubernetes/tencent-cloud.json
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
imagePullPolicy: Always
|
||||
name: external-dns
|
||||
resources: {}
|
||||
terminationMessagePath: /dev/termination-log
|
||||
terminationMessagePolicy: File
|
||||
volumeMounts:
|
||||
- mountPath: /etc/kubernetes
|
||||
name: config-volume
|
||||
readOnly: true
|
||||
dnsPolicy: ClusterFirst
|
||||
hostAliases:
|
||||
- hostnames:
|
||||
- privatedns.internal.tencentcloudapi.com
|
||||
- dnspod.internal.tencentcloudapi.com
|
||||
ip: 169.254.0.95
|
||||
restartPolicy: Always
|
||||
schedulerName: default-scheduler
|
||||
securityContext: {}
|
||||
serviceAccount: external-dns
|
||||
serviceAccountName: external-dns
|
||||
terminationGracePeriodSeconds: 30
|
||||
volumes:
|
||||
- configMap:
|
||||
defaultMode: 420
|
||||
items:
|
||||
- key: tencent-cloud.json
|
||||
path: tencent-cloud.json
|
||||
name: external-dns
|
||||
name: config-volume
|
||||
```
|
||||
|
||||
# Example
|
||||
|
||||
## Service
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.com
|
||||
external-dns.alpha.kubernetes.io/internal-hostname: nginx-internal.external-dns-test.com
|
||||
external-dns.alpha.kubernetes.io/ttl: "600"
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- port: 80
|
||||
name: http
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: nginx
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: http
|
||||
```
|
||||
|
||||
`nginx.external-dns-test.com` will record to the Loadbalancer VIP.
|
||||
`nginx-internal.external-dns-test.com` will record to the ClusterIP.
|
||||
all of the DNS Record ttl will be 600.
|
||||
|
||||
# Attention
|
||||
|
||||
This makes ExternalDNS safe for running in environments where there are other records managed via other means.
|
||||
|
@ -36,7 +36,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains
|
||||
@ -107,7 +107,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains
|
||||
|
@ -44,7 +44,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress # ingress is also possible
|
||||
@ -116,7 +116,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
@ -251,6 +251,7 @@ spec:
|
||||
- http:
|
||||
paths:
|
||||
- path: /apple
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: example-service
|
||||
|
@ -66,7 +66,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --provider=vinyldns
|
||||
- --source=service
|
||||
@ -137,7 +137,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --provider=vinyldns
|
||||
- --source=service
|
||||
|
@ -42,7 +42,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
@ -106,7 +106,7 @@ spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
image: registry.k8s.io/external-dns/external-dns:v0.13.4
|
||||
args:
|
||||
- --source=service # ingress is also possible
|
||||
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
|
||||
|
@ -73,9 +73,11 @@ type DomainFilter struct {
|
||||
|
||||
// prepareFilters provides consistent trimming for filters/exclude params
|
||||
func prepareFilters(filters []string) []string {
|
||||
fs := make([]string, len(filters))
|
||||
for i, domain := range filters {
|
||||
fs[i] = strings.ToLower(strings.TrimSuffix(strings.TrimSpace(domain), "."))
|
||||
var fs []string
|
||||
for _, filter := range filters {
|
||||
if domain := strings.ToLower(strings.TrimSuffix(strings.TrimSpace(filter), ".")); domain != "" {
|
||||
fs = append(fs, domain)
|
||||
}
|
||||
}
|
||||
return fs
|
||||
}
|
||||
@ -98,7 +100,7 @@ func NewRegexDomainFilter(regexDomainFilter *regexp.Regexp, regexDomainExclusion
|
||||
// Match checks whether a domain can be found in the DomainFilter.
|
||||
// RegexFilter takes precedence over Filters
|
||||
func (df DomainFilter) Match(domain string) bool {
|
||||
if df.regex != nil && df.regex.String() != "" {
|
||||
if df.regex != nil && df.regex.String() != "" || df.regexExclusion != nil && df.regexExclusion.String() != "" {
|
||||
return matchRegex(df.regex, df.regexExclusion, domain)
|
||||
}
|
||||
|
||||
@ -113,12 +115,13 @@ func matchFilter(filters []string, domain string, emptyval bool) bool {
|
||||
return emptyval
|
||||
}
|
||||
|
||||
strippedDomain := strings.ToLower(strings.TrimSuffix(domain, "."))
|
||||
for _, filter := range filters {
|
||||
strippedDomain := strings.ToLower(strings.TrimSuffix(domain, "."))
|
||||
|
||||
if filter == "" {
|
||||
return emptyval
|
||||
} else if strings.HasPrefix(filter, ".") && strings.HasSuffix(strippedDomain, filter) {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(filter, ".") && strings.HasSuffix(strippedDomain, filter) {
|
||||
return true
|
||||
} else if strings.Count(strippedDomain, ".") == strings.Count(filter, ".") {
|
||||
if strippedDomain == filter {
|
||||
@ -146,35 +149,32 @@ func matchRegex(regex *regexp.Regexp, negativeRegex *regexp.Regexp, domain strin
|
||||
|
||||
// MatchParent checks wether DomainFilter matches a given parent domain.
|
||||
func (df DomainFilter) MatchParent(domain string) bool {
|
||||
if !df.IsConfigured() {
|
||||
if matchFilter(df.exclude, domain, false) {
|
||||
return false
|
||||
}
|
||||
if len(df.Filters) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
strippedDomain := strings.ToLower(strings.TrimSuffix(domain, "."))
|
||||
for _, filter := range df.Filters {
|
||||
if strings.HasPrefix(filter, ".") {
|
||||
if filter == "" || strings.HasPrefix(filter, ".") {
|
||||
// We don't check parents if the filter is prefixed with "."
|
||||
continue
|
||||
}
|
||||
|
||||
if filter == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
strippedDomain := strings.ToLower(strings.TrimSuffix(domain, "."))
|
||||
if strings.HasSuffix(filter, "."+strippedDomain) && !matchFilter(df.exclude, domain, false) {
|
||||
if strings.HasSuffix(filter, "."+strippedDomain) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IsConfigured returns true if DomainFilter is configured, false otherwise
|
||||
// IsConfigured returns true if any inclusion or exclusion rules have been specified.
|
||||
func (df DomainFilter) IsConfigured() bool {
|
||||
if df.regex != nil && df.regex.String() != "" {
|
||||
return true
|
||||
} else if len(df.Filters) == 1 {
|
||||
return df.Filters[0] != ""
|
||||
} else if df.regexExclusion != nil && df.regexExclusion.String() != "" {
|
||||
return true
|
||||
}
|
||||
return len(df.Filters) > 0
|
||||
return len(df.Filters) > 0 || len(df.exclude) > 0
|
||||
}
|
||||
|
@ -379,15 +379,15 @@ func TestPrepareFiltersStripsWhitespaceAndDotSuffix(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
[]string{},
|
||||
[]string{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[]string{""},
|
||||
[]string{""},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[]string{" ", " ", ""},
|
||||
[]string{"", "", ""},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[]string{" foo ", " bar. ", "baz."},
|
||||
@ -429,7 +429,7 @@ func TestDomainFilterIsConfigured(t *testing.T) {
|
||||
{
|
||||
[]string{"", ""},
|
||||
[]string{""},
|
||||
true,
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]string{" . "},
|
||||
@ -446,6 +446,11 @@ func TestDomainFilterIsConfigured(t *testing.T) {
|
||||
[]string{" thisdoesntmatter.com "},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{""},
|
||||
[]string{" thisdoesntmatter.com "},
|
||||
true,
|
||||
},
|
||||
} {
|
||||
t.Run("test IsConfigured", func(t *testing.T) {
|
||||
df := NewDomainFilterWithExclusions(tt.filters, tt.exclude)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user