Skip to content

Commit

Permalink
update repo.
Browse files Browse the repository at this point in the history
Signed-off-by: morvencao <[email protected]>
  • Loading branch information
morvencao committed Feb 25, 2022
1 parent 6e28b10 commit 4b68580
Show file tree
Hide file tree
Showing 21 changed files with 231 additions and 620 deletions.
3 changes: 0 additions & 3 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,3 @@
*.out

vendor/*

# GOPATH
.go/*
23 changes: 23 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Build the sidecar-injector binary
FROM golang:1.17 as builder

WORKDIR /workspace
# Copy the Go Modules manifests
COPY go.mod go.mod
COPY go.sum go.sum
# cache deps before building and copying source so that we don't need to re-download as much
# and so that source changes don't invalidate our downloaded layer
RUN go mod download

# Copy the go source
COPY cmd/ cmd/

# Build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o sidecar-injector ./cmd

FROM alpine:latest
WORKDIR /
COPY --from=builder /workspace/sidecar-injector .
USER 65532:65532

ENTRYPOINT ["/sidecar-injector"]
192 changes: 97 additions & 95 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,98 +1,100 @@
# Image URL to use all building/pushing image targets;
# Use your own docker registry and image name for dev/test by overridding the
# IMAGE_REPO, IMAGE_NAME and IMAGE_TAG environment variable.
IMAGE_REPO ?= docker.io/morvencao
IMAGE_NAME ?= sidecar-injector

# Github host to use for checking the source tree;
# Override this variable ue with your own value if you're working on forked repo.
GIT_HOST ?= github.com/morvencao

PWD := $(shell pwd)
BASE_DIR := $(shell basename $(PWD))

# Keep an existing GOPATH, make a private one if it is undefined
GOPATH_DEFAULT := $(PWD)/.go
export GOPATH ?= $(GOPATH_DEFAULT)
TESTARGS_DEFAULT := "-v"
export TESTARGS ?= $(TESTARGS_DEFAULT)
DEST := $(GOPATH)/src/$(GIT_HOST)/$(BASE_DIR)
IMAGE_TAG ?= $(shell date +v%Y%m%d)-$(shell git describe --match=$(git rev-parse --short=8 HEAD) --tags --always --dirty)


LOCAL_OS := $(shell uname)
ifeq ($(LOCAL_OS),Linux)
TARGET_OS ?= linux
XARGS_FLAGS="-r"
else ifeq ($(LOCAL_OS),Darwin)
TARGET_OS ?= darwin
XARGS_FLAGS=
# Setting SHELL to bash allows bash commands to be executed by recipes.
# This is a requirement for 'setup-envtest.sh' in the test target.
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec

# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq (,$(shell go env GOBIN))
GOBIN=$(shell go env GOPATH)/bin
else
$(error "This system's OS $(LOCAL_OS) isn't recognized/supported")
GOBIN=$(shell go env GOBIN)
endif

all: fmt lint test build image

ifeq (,$(wildcard go.mod))
ifneq ("$(realpath $(DEST))", "$(realpath $(PWD))")
$(error Please run 'make' from $(DEST). Current directory is $(PWD))
endif
endif

############################################################
# format section
############################################################

fmt:
@echo "Run go fmt..."

############################################################
# lint section
############################################################

lint:
@echo "Runing the golangci-lint..."

############################################################
# test section
############################################################

test:
@echo "Running the tests for $(IMAGE_NAME)..."
@go test $(TESTARGS) ./...

############################################################
# build section
############################################################

build:
@echo "Building the $(IMAGE_NAME) binary..."
@CGO_ENABLED=0 go build -o build/_output/bin/$(IMAGE_NAME) ./cmd/

build-linux:
@echo "Building the $(IMAGE_NAME) binary for Docker (linux)..."
@GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o build/_output/linux/bin/$(IMAGE_NAME) ./cmd/

############################################################
# image section
############################################################

image: build-image push-image

build-image: build-linux
@echo "Building the docker image: $(IMAGE_REPO)/$(IMAGE_NAME):$(IMAGE_TAG)..."
@docker build -t $(IMAGE_REPO)/$(IMAGE_NAME):$(IMAGE_TAG) -f build/Dockerfile .

push-image: build-image
@echo "Pushing the docker image for $(IMAGE_REPO)/$(IMAGE_NAME):$(IMAGE_TAG) and $(IMAGE_REPO)/$(IMAGE_NAME):latest..."
@docker tag $(IMAGE_REPO)/$(IMAGE_NAME):$(IMAGE_TAG) $(IMAGE_REPO)/$(IMAGE_NAME):latest
@docker push $(IMAGE_REPO)/$(IMAGE_NAME):$(IMAGE_TAG)
@docker push $(IMAGE_REPO)/$(IMAGE_NAME):latest

############################################################
# clean section
############################################################
clean:
@rm -rf build/_output

.PHONY: all fmt lint check test build image clean
# Tools for deploy
KUBECTL?=kubectl
PWD=$(shell pwd)
KUSTOMIZE?=$(PWD)/$(PERMANENT_TMP_GOPATH)/bin/kustomize
KUSTOMIZE_VERSION?=v3.5.4
KUSTOMIZE_ARCHIVE_NAME?=kustomize_$(KUSTOMIZE_VERSION)_$(GOHOSTOS)_$(GOHOSTARCH).tar.gz
kustomize_dir:=$(dir $(KUSTOMIZE))

IMAGE = quay.io/morvencao/sidecar-injector:latest

all: build
.PHONY: all

##@ General

# The help target prints out all targets with their descriptions organized
# beneath their categories. The categories are represented by '##@' and the
# target descriptions by '##'. The awk commands is responsible for reading the
# entire set of makefiles included in this invocation, looking for lines of the
# file as xyz: ## something, and then pretty-format the target and help. Then,
# if there's a line with ##@ something, that gets pretty-printed as a category.
# More info on the usage of ANSI control characters for terminal formatting:
# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
# More info on the awk command:
# http://linuxcommand.org/lc3_adv_awk.php

.PHONY: help
help: ## Display this help.
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

##@ Development

.PHONY: fmt
fmt: ## Run go fmt against code.
go fmt ./...

.PHONY: vet
vet: ## Run go vet against code.
go vet ./...

.PHONY: test
test: fmt vet ## Run tests.
go test ./... -coverprofile cover.out

##@ Build

.PHONY: build
build: fmt vet ## Build binary.
go build -o bin/sidecar-injector ./cmd/

.PHONY: docker-build
docker-build: test ## Build docker image.
docker build -t ${IMAGE} .

.PHONY: docker-push
docker-push: ## Push docker image.
docker push ${IMAGE}

##@ Deployment

deploy: kustomize
cp deploy/kustomization.yaml deploy/kustomization.yaml.tmp
cd deploy && $(KUSTOMIZE) edit set image sidecar-injector=$(IMAGE)
$(KUSTOMIZE) build deploy | $(KUBECTL) apply -f -
mv deploy/kustomization.yaml.tmp deploy/kustomization.yaml

undeploy: kustomize
$(KUSTOMIZE) build deploy | $(KUBECTL) delete --ignore-not-found -f -

KUSTOMIZE = $(shell pwd)/bin/kustomize
.PHONY: kustomize
kustomize: ## Download kustomize locally if necessary.
$(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/[email protected])

# go-get-tool will 'go get' any package $2 and install it to $1.
PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
define go-get-tool
@[ -f $(1) ] || { \
set -e ;\
TMP_DIR=$$(mktemp -d) ;\
cd $$TMP_DIR ;\
go mod init tmp ;\
echo "Downloading $(2)" ;\
GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\
rm -rf $$TMP_DIR ;\
}
endef
121 changes: 38 additions & 83 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
# Kubernetes Mutating Webhook for Sidecar Injection
# kube-sidecar-injector

[![Go Report Card](https://goreportcard.com/badge/github.com/morvencao/kube-mutating-webhook-tutorial)](https://goreportcard.com/report/github.com/morvencao/kube-mutating-webhook-tutorial)
[![GoDoc](https://godoc.org/github.com/morvencao/kube-mutating-webhook-tutorial?status.svg)](https://godoc.org/github.com/morvencao/kube-mutating-webhook-tutorial)

This tutoral shows how to build and deploy a [MutatingAdmissionWebhook](https://kubernetes.io/docs/admin/admission-controllers/#mutatingadmissionwebhook-beta-in-19) that injects a nginx sidecar container into pod prior to persistence of the object.
This repo is used for tutoral to create a Kubernetes [MutatingAdmissionWebhook](https://kubernetes.io/docs/admin/admission-controllers/#mutatingadmissionwebhook-beta-in-19) that injects a nginx sidecar container into pod prior to persistence of the object.

## Prerequisites

- [git](https://git-scm.com/downloads)
- [go](https://golang.org/dl/) version v1.12+
- [docker](https://docs.docker.com/install/) version 17.03+
- [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) version v1.11.3+
- Access to a Kubernetes v1.11.3+ cluster with the `admissionregistration.k8s.io/v1beta1` API enabled. Verify that by the following command:
- [go](https://golang.org/dl/) version v1.17+
- [docker](https://docs.docker.com/install/) version 19.03+
- [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) version v1.19+
- Access to a Kubernetes v1.19+ cluster with the `admissionregistration.k8s.io/v1` API enabled. Verify that by the following command:

```
kubectl api-versions | grep admissionregistration.k8s.io
Expand All @@ -24,111 +21,69 @@ admissionregistration.k8s.io/v1beta1

> Note: In addition, the `MutatingAdmissionWebhook` and `ValidatingAdmissionWebhook` admission controllers should be added and listed in the correct order in the admission-control flag of kube-apiserver.
## Build

1. Build binary
## Build and Deploy

```
# make build
```
1. Build and push docker image:

2. Build docker image

```
# make build-image
```bash
make docker-build docker-push IMAGE=quay.io/<your_quayio_username>/sidecar-injector:latest
```

3. push docker image
2. Deploy the kube-sidecar-injector to kubernetes cluster:

```bash
make deploy IMAGE=quay.io/<your_quayio_username>/sidecar-injector:latest
```
# make push-image
```

> Note: log into the docker registry before pushing the image.
## Deploy

1. Create namespace `sidecar-injector` in which the sidecar injector webhook is deployed:

```
# kubectl create ns sidecar-injector
```

2. Create a signed cert/key pair and store it in a Kubernetes `secret` that will be consumed by sidecar injector deployment:

```
# ./deploy/webhook-create-signed-cert.sh \
--service sidecar-injector-webhook-svc \
--secret sidecar-injector-webhook-certs \
--namespace sidecar-injector
```
3. Verify the kube-sidecar-injector is up and running:

3. Patch the `MutatingWebhookConfiguration` by set `caBundle` with correct value from Kubernetes cluster:

```
# cat deploy/mutatingwebhook.yaml | \
deploy/webhook-patch-ca-bundle.sh > \
deploy/mutatingwebhook-ca-bundle.yaml
```

4. Deploy resources:

```
# kubectl create -f deploy/nginxconfigmap.yaml
# kubectl create -f deploy/configmap.yaml
# kubectl create -f deploy/deployment.yaml
# kubectl create -f deploy/service.yaml
# kubectl create -f deploy/mutatingwebhook-ca-bundle.yaml
```

## Verify

1. The sidecar inject webhook should be in running state:

```
```bash
# kubectl -n sidecar-injector get pod
NAME READY STATUS RESTARTS AGE
sidecar-injector-webhook-deployment-7c8bc5f4c9-28c84 1/1 Running 0 30s
# kubectl -n sidecar-injector get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
sidecar-injector-webhook-deployment 1/1 1 1 67s
# kubectl -n sidecar-injector get pod
NAME READY STATUS RESTARTS AGE
sidecar-injector-7c8bc5f4c9-28c84 1/1 Running 0 30s
```

2. Create new namespace `injection` and label it with `sidecar-injector=enabled`:
## How to use

1. Create a new namespace `test-ns` and label it with `sidecar-injector=enabled`:

```
# kubectl create ns injection
# kubectl label namespace injection sidecar-injection=enabled
# kubectl create ns test-ns
# kubectl label namespace test-ns sidecar-injection=enabled
# kubectl get namespace -L sidecar-injection
NAME STATUS AGE SIDECAR-INJECTION
default Active 26m
injection Active 13s enabled
test-ns Active 13s enabled
kube-public Active 26m
kube-system Active 26m
sidecar-injector Active 17m
```

3. Deploy an app in Kubernetes cluster, take `alpine` app as an example
2. Deploy an app in Kubernetes cluster, take `alpine` app as an example

```
# kubectl run alpine --image=alpine --restart=Never -n injection --overrides='{"apiVersion":"v1","metadata":{"annotations":{"sidecar-injector-webhook.morven.me/inject":"yes"}}}' --command -- sleep infinity
```bash
kubectl -n test-ns run alpine \
--image=alpine \
--restart=Never \
--overrides='{"apiVersion":"v1","metadata":{"annotations":{"sidecar-injector-webhook.morven.me/inject":"yes"}}}' \
--command -- sleep infinity
```

4. Verify sidecar container is injected:
3. Verify sidecar container is injected:

```
# kubectl get pod
# kubectl -n test-ns get pod
NAME READY STATUS RESTARTS AGE
alpine 2/2 Running 0 1m
# kubectl -n injection get pod alpine -o jsonpath="{.spec.containers[*].name}"
alpine 2/2 Running 0 10s
# kubectl -n test-ns get pod alpine -o jsonpath="{.spec.containers[*].name}"
alpine sidecar-nginx
```

## Troubleshooting

Sometimes you may find that pod is injected with sidecar container as expected, check the following items:

1. The sidecar-injector webhook is in running state and no error logs.
2. The namespace in which application pod is deployed has the correct labels as configured in `mutatingwebhookconfiguration`.
3. Check the `caBundle` is patched to `mutatingwebhookconfiguration` object by checking if `caBundle` fields is empty.
4. Check if the application pod has annotation `sidecar-injector-webhook.morven.me/inject":"yes"`.
1. The sidecar-injector pod is in running state and no error logs.
2. The namespace in which application pod is deployed has the correct labels(`sidecar-injector=enabled`) as configured in `mutatingwebhookconfiguration`.
3. Check if the application pod has annotation `sidecar-injector-webhook.morven.me/inject":"yes"`.
Binary file added bin/kustomize
Binary file not shown.
Loading

0 comments on commit 4b68580

Please sign in to comment.