Bu örnekte gizli ve gizli olmayan yapılandırma dosyalarını Pod'lara bağlayacağız. Sırlar için kullanacağımız iki yol var. secret.yaml dosyasını kullanma ve Kubernetes kümesinde gizli dizileri manuel olarak oluşturma. İlk seçenekteki sorun, secret.yaml dosyasını Git'e kaydetmenizdir. İkinci seçenek için sırların Kubernetes'te olduğundan emin olun, aksi takdirde Pod çalışmayacaktır.


Bağlayacağımız dosyalar:



Yapı


secret klasördeki dosyalar ve .env dosyası Git'e atılmıyor çünkü bunlar development ortamı için gizli bilgiler içeriyor.


├── Makefile
├── config
│ └── finders.yaml
├── deploy
│ └── k8s
│ ├── configmap.yaml
│ ├── deployment.yaml
│ └── secret.yaml
├── docker
│ └── dev
│ └── Dockerfile
├── .env
├── .env.dist
├── .gitignore
├── main.go
└── secret
├── credentials.conf
└── keys.yaml

Dosyalar


Makefile


## Build application binary.
.PHONY: build
build:
go build -race -ldflags "-s -w" -o bin/main main.go

## Build application binary and run it.
.PHONY: run
run: build
bin/main

## -----------------------------------------------------------------------------------

## Build, tag and push application image to registry then clean up.
.PHONY: push
push:
docker build -t you/address-finder:latest -f docker/dev/Dockerfile .
docker push you/address-finder:latest
docker rmi you/address-finder:latest
docker system prune --volumes --force

## Deploy application to kubernetes cluster.
.PHONY: deploy
deploy:
# This secret command is not needed for manual secret interactions (preferred).
kubectl apply -f deploy/k8s/secret.yaml
kubectl apply -f deploy/k8s/configmap.yaml
kubectl apply -f deploy/k8s/deployment.yaml

.env


ENV_VAR_X=x_secret_from_repo_non_dist
ENV_VAR_Y=y_secret_from_repo_non_dist
ENV_VAR_Z=z_non_secret_from_repo_non_dist

.env.dist


ENV_VAR_X=x_secret_from_repo_dist
ENV_VAR_Y=y_secret_from_repo_dist
ENV_VAR_Z=z_non_secret_from_repo_dist

.gitignore


secret/
.env

main.go


package main

import (
"fmt"
"io/ioutil"
"log"
"os"
"time"

"github.com/joho/godotenv"
)

func main() {
fmt.Println("--- Secret credentials file ---------------------")
scrConf, err := ioutil.ReadFile("secret/credentials.conf")
if err != nil {
log.Fatalln(err)
}
fmt.Println(string(scrConf))

fmt.Println("--- Secret keys file ----------------------------")
kysConf, err := ioutil.ReadFile("secret/keys.yaml")
if err != nil {
log.Fatalln(err)
}
fmt.Println(string(kysConf))

fmt.Println("--- Public finders file -------------------------")
finConf, err := ioutil.ReadFile("config/finders.yaml")
if err != nil {
log.Fatalln(err)
}
fmt.Println(string(finConf))

fmt.Println("--- System environment variables ----------------")
_ = godotenv.Load()
fmt.Println(value("ENV_VAR_X"))
fmt.Println(value("ENV_VAR_Y"))
fmt.Println(value("ENV_VAR_Z"))

time.Sleep(time.Hour)
}

func value(key string) string {
v, ok := os.LookupEnv(key)
if !ok {
log.Fatalf("the environment var %s was not found", key)
}

return v
}

Dockerfile


#
# STAGE 1: build
#
FROM golang:1.15-alpine3.12 as build

WORKDIR /source
COPY . .

RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o bin/main main.go

#
# STAGE 2: run
#
FROM alpine:3.12 as run

COPY --from=build /source/bin/main /main

ENTRYPOINT ["/main"]

finders.yaml


finders:
- name: postcode
api: https://api.postcodes.io/postcodes
method: GET
- name: ip
api: http://ip-api.com/json
method: GET

credentials.yaml


title: this is credentials.conf secret content (local)
line_1: hello (local)

line_2: world (local)

keys.yaml


KEY_1: key 1 (local)
KEY_2: key 2 (local)

configmap.yaml


apiVersion: v1
kind: ConfigMap

metadata:
name: address-finder-configmap

data:
finders.yaml: |
finders:
- name: address
api: https://api.getAddress.io/find
method: GET
- name: postcode
api: https://api.postcodes.io/postcodes
method: GET
- name: ip
api: http://ip-api.com/json
method: GET

deployment.yaml


apiVersion: apps/v1
kind: Deployment

metadata:
name: address-finder-deployment
labels:
app: address-finder

spec:
replicas: 1
selector:
matchLabels:
app: address-finder
template:
metadata:
labels:
app: address-finder
spec:
containers:
- name: golang
image: you/address-finder:latest
volumeMounts:
# Refers to a single file
- name: config
mountPath: ./config/finders.yaml
subPath: finders.yaml
readOnly: true
# Refers to all the secret files in a folder
- name: secret
mountPath: ./secret
readOnly: true
# Refers to single secret file to app root
- name: dotenv
mountPath: ./.env
subPath: .env # Without this, .env will be created as a folder, not a file!
readOnly: true
volumes:
- name: config
configMap:
name: address-finder-configmap
- name: secret
secret:
secretName: address-finder-secret
- name: dotenv
secret:
secretName: address-finder-secret
items:
- key: .env
path: ./.env

secret.yaml


Unutmayın, bu sırları ele almak için tercih edilmeyen yoldur. Seçenek 2'de bu dosyayı silip, manuel olarak address-finder-secret bileşenini yaratacağız. Burada gördüğünüz şey 1. seçenek.


apiVersion: v1
kind: Secret

metadata:
name: address-finder-secret

type: Opaque

data:
credentials.conf: | # cat secret/credentials.conf | base64
dGl0bGU6IHRoaXMgaXMgY3JlZGVudGlhbHMuY29uZiBzZWNyZXQgY29udGVudCAobG9jYWwpCmxpbmVfMTogaGVsbG8gKGxvY2FsKQoKbGluZV8yOiB3b3JsZCAobG9jYWwpCg==
keys.yaml: | # cat secret/keys.yaml | base64
S0VZXzE6IGtleSAxIChsb2NhbCkKS0VZXzI6IGtleSAyIChsb2NhbCkK
.env: | # cat .env | base64
RU5WX1ZBUl9YPXhfc2VjcmV0X2Zyb21fcmVwb19ub25fZGlzdApFTlZfVkFSX1k9eV9zZWNyZXRfZnJvbV9yZXBvX25vbl9kaXN0CkVOVl9WQVJfWj16X25vbl9zZWNyZXRfZnJvbV9yZXBvX25vbl9kaXN0Cg==

Yerel test


Uygulamayı yerel ortamda çalıştırdıysak, terminal çıktısı aşağıdaki şekilde görünürdü. Gördüğünüz gibi içerik yukarıda listelediğimiz dosyalarla eşleşiyor.


$ make run
go build -race -ldflags "-s -w" -o bin/main main.go
bin/main
--- Secret credentials file ---------------------
title: this is credentials.conf secret content (local)
line_1: hello (local)

line_2: world (local)

--- Secret keys file ----------------------------
KEY_1: key 1 (local)
KEY_2: key 2 (local)

--- Public finders file -------------------------
finders:
- name: postcode
api: https://api.postcodes.io/postcodes
method: GET
- name: ip
api: http://ip-api.com/json
method: GET

--- System environment variables ----------------
x_secret_from_repo_non_dist
y_secret_from_repo_non_dist
z_non_secret_from_repo_non_dist

Dağıtım


Seçenek 1


Burada secret.yaml dosyasını Git repositoryde tutuyoruz ve otomatik olarak gizlilerin yaratılmasını sağlıyoruz. Tavsiye edilmez! Aşağıda uygulamayı dağıtıyoruz.


$ make deploy
# This secret command is not needed for manual secret interactions (preferred).
kubectl apply -f deploy/k8s/secret.yaml
secret/address-finder-secret created
kubectl apply -f deploy/k8s/configmap.yaml
configmap/address-finder-configmap created
kubectl apply -f deploy/k8s/deployment.yaml
deployment.apps/address-finder-deployment created

Aşağıdaki uygulama günlüklerini kontrol ediyoruz.


$ kubectl logs pod/address-finder-deployment-7f6d6959db-w57gn
--- Secret credentials file ---------------------
title: this is credentials.conf secret content (local)
line_1: hello (local)

line_2: world (local)

--- Secret keys file ----------------------------
KEY_1: key 1 (local)
KEY_2: key 2 (local)

--- Public finders file -------------------------
finders:
- name: address
api: https://api.getAddress.io/find
method: GET
- name: postcode
api: https://api.postcodes.io/postcodes
method: GET
- name: ip
api: http://ip-api.com/json
method: GET

--- System environment variables ----------------
x_secret_from_repo_non_dist
y_secret_from_repo_non_dist
z_non_secret_from_repo_non_dist

Yukarıda görebileceğiniz gibi, sırların kodu çözüldü (yerel kopya olarak kodlandılar) ve Pod'a eklendiler. Ayrıca, finders config dosyası, Git repository yerine configmap.yaml dosyasından geliyor. Ortam değişkenleri, ortamdan değil .env dosyasından geliyor çünkü onları normal ortam değişkenleri olarak oluşturmadık. Pod içeriğini kontrol edelim.


$ kubectl exec -it pod/address-finder-deployment-7f6d6959db-w57gn sh 
/ # ls -la
total 1816
-rw-r--r-- 1 root root 118 Nov 26 19:26 .env
drwxr-xr-x 2 root root 4096 Nov 26 19:26 config
drwxrwxrwt 3 root root 140 Nov 26 19:26 secret
...

/ # cat .env
ENV_VAR_X=x_secret_from_repo_non_dist
ENV_VAR_Y=y_secret_from_repo_non_dist
ENV_VAR_Z=z_non_secret_from_repo_non_dist

/ # ls -l config/
total 4
-rw-r--r-- 1 root root 223 Nov 26 19:26 finders.yaml
/ # cat config/finders.yaml
finders:
- name: address
api: https://api.getAddress.io/find
method: GET
- name: postcode
api: https://api.postcodes.io/postcodes
method: GET
- name: ip
api: http://ip-api.com/json
method: GET

/ # ls -l secret/
total 0
lrwxrwxrwx 1 root root 23 Nov 26 19:26 credentials.conf -> ..data/credentials.conf
lrwxrwxrwx 1 root root 16 Nov 26 19:26 keys.yaml -> ..data/keys.yaml
/ # cat secret/credentials.conf
title: this is credentials.conf secret content (local)
line_1: hello (local)

line_2: world (local)
/ # cat secret/keys.yaml
KEY_1: key 1 (local)
KEY_2: key 2 (local)

Sır bileşenlerinin neye benzediğini kontrol edelim. Bunu daha sonra seçenek 2'de manuel olarak oluşturacağız.


$ kubectl get secrets
NAME TYPE DATA AGE
address-finder-secret Opaque 3 10m

$ kubectl describe secret address-finder-secret
Name: address-finder-secret
Namespace: default
Labels:
Annotations:

Type: Opaque

Data
====
keys.yaml: 42 bytes
.env: 118 bytes
credentials.conf: 100 bytes

Seçenek 2


Burada secret.yaml dosyasına sahip değiliz ve gizlileri manuel olarak dağıtımdan önce Kubernetes içinde yaratacağız - tavsiye edilen! Burada gizli dosyaları manuel olarak oluşturuyoruz. Dosyalara daha önceden sahip olduğumdan, sadece içerikleri gösteriyor.


$ cat ./Desktop/credentials.conf 
title: this is credentials.conf secret content (desktop)
line_1: hello (desktop)

line_2: world (dektop)

$ cat ./Desktop/keys.yaml
KEY_1: key 1 (desktop)
KEY_2: key 2 (desktop)

$ cat ./Desktop/.env
ENV_VAR_X=x_secret_from_desktop
ENV_VAR_Y=y_secret_from_desktop
ENV_VAR_Z=z_non_secret_from_desktop

Bu dosyaları Kubernetes gizli bileşenine çevirelim.


$ kubectl create secret generic address-finder-secret --save-config \
> --from-file=./Desktop/credentials.conf \
> --from-file=./Desktop/keys.yaml \
> --from-file=./Desktop/.env

Aşağıda görebileceğiniz gibi, bu komut daha önce seçenek 1'de oluşturulan secret.yaml ile eşleşmektedir.


$ kubectl get secrets
NAME TYPE DATA AGE
address-finder-secret Opaque 3 21s

$ kubectl describe secret address-finder-secret
Name: address-finder-secret
Namespace: default
Labels:
Annotations:

Type: Opaque

Data
====
.env: 101 bytes
credentials.conf: 105 bytes
keys.yaml: 47 bytes

Şimdi uygulamayı dağıtalım. Make komutundan secret.yaml komutunu kaldırdım.


$ make deploy
kubectl apply -f deploy/k8s/configmap.yaml
configmap/address-finder-configmap created
kubectl apply -f deploy/k8s/deployment.yaml

Uygulama günlüğünü/çıktısını görelim. Sırlara odaklanın.


$ kubectl logs pod/address-finder-deployment-7f6d6959db-f4rcr
--- Secret credentials file ---------------------
title: this is credentials.conf secret content (desktop)
line_1: hello (desktop)

line_2: world (dektop)

--- Secret keys file ----------------------------
KEY_1: key 1 (desktop)
KEY_2: key 2 (desktop)


--- Public finders file -------------------------
finders:
- name: address
api: https://api.getAddress.io/find
method: GET
- name: postcode
api: https://api.postcodes.io/postcodes
method: GET
- name: ip
api: http://ip-api.com/json
method: GET

--- System environment variables ----------------
x_secret_from_desktop
y_secret_from_desktop
z_non_secret_from_desktop

Yukarıda gördüğünüz gibi, iyi çalıştı.


Sırları daha sonra güncellemek isterseniz, kubectl edit secret address-finder-secret komutunu kullanabilirsiniz. Base64 kodlu veri gerektirdiğini unutmayın.


Base64 kodlama/kod çözme için aşağıdaki komutu kullanabilirsiniz.


// encode
$ cat {file-path} | base64

// decode
$ echo -n {plain-secret-goes-here} | base64