Skip to content

Instantly share code, notes, and snippets.

@amcginlay
Last active March 18, 2021 11:17
Show Gist options
  • Save amcginlay/8de09f9a023b5f860d01f561562bb3df to your computer and use it in GitHub Desktop.
Save amcginlay/8de09f9a023b5f860d01f561562bb3df to your computer and use it in GitHub Desktop.
[work in progress]
INSTRUCTIONS
# we'll assume the AWS LOAD BALANCER (INGRESS) CONTROLLER is already installed
app_name=alpha
app_version=1.0.0
export AWS_DEFAULT_REGION=$(curl --silent curl --silent http://169.254.169.254/latest/meta-data/placement/region)
eksctl utils associate-iam-oidc-provider \
--cluster dev \
--approve
eksctl create iamserviceaccount \
--cluster dev \
--namespace appmesh-system \
--name appmesh-controller \
--attach-policy-arn arn:aws:iam::aws:policy/AWSCloudMapFullAccess,arn:aws:iam::aws:policy/AWSAppMeshFullAccess \
--override-existing-serviceaccounts \
--approve
helm repo add eks https://aws.github.io/eks-charts
kubectl create ns appmesh-system
helm upgrade -i appmesh-controller eks/appmesh-controller \
--namespace appmesh-system \
--set region=${AWS_DEFAULT_REGION} \
--set serviceAccount.create=false \
--set serviceAccount.name=appmesh-controller
# aws appmesh create-mesh \
# --mesh-name ${app_name} \
# --spec egressFilter={type=DROP_ALL}
cat > ~/environment/${app_name}/mesh.yml << EOF
apiVersion: appmesh.k8s.aws/v1beta2
kind: Mesh
metadata:
name: ${app_name}
spec:
namespaceSelector:
matchLabels:
mesh: ${app_name}
EOF
kubectl apply -f ~/environment/${app_name}/mesh.yml
mkdir -p ~/environment/${app_name}/manifests
cat > ~/environment/${app_name}/main.go << EOF
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"regexp"
"strconv"
"os"
)
func main() {
port := os.Getenv("PORT")
if len(port) == 0 {
port = "8080"
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
u, err := url.Parse(r.URL.String())
if err != nil {
panic(err)
}
matched, err := regexp.MatchString(\`^/[a-z]+$\`, u.Path) // # backslash escaped to support heredocs
if err != nil {
panic(err)
}
if !matched {
fmt.Fprintf(w, "failure: sequence of lowercase alphabetic characters expected on path\n")
return
}
head := u.Path[1:2]
if len(u.Path) <= 2 {
// we've reached the final character in the word so that's our response
fmt.Fprintf(w, head)
return
}
// there's more characters to process so fire off a GET request to the appropriate domain with the remainder of the word in the path
tail := u.Path[2:]
letter := tail[0:1]
resp, err := http.Get("http://alpha-" + letter + ":" + port + "/" + tail)
//resp, err := http.Get("http://localhost:" + port + "/" + tail) // NOTE for test purposes
if err != nil {
panic(err)
}
defer resp.Body.Close()
result := ""
if resp.StatusCode == http.StatusOK {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
result = string(body)
} else {
result = "[" + strconv.Itoa(resp.StatusCode) + "]"
}
fmt.Fprintf(w, result + head)
})
fmt.Printf("Starting server at port " + port + "\n")
if err := http.ListenAndServe(":" + port, nil); err != nil {
log.Fatal(err)
}
}
EOF
cat > ~/environment/${app_name}/Dockerfile << EOF
FROM golang:alpine as builder
COPY . .
RUN go build -o /go/bin/app
FROM alpine:latest
COPY --from=builder /go/bin/app app
ENV PORT=80
EXPOSE 80
ENTRYPOINT ["./app"]
EOF
docker build -t ${app_name} ~/environment/${app_name}/
repo_uri=$( \
aws ecr create-repository \
--repository-name ${app_name} \
--image-scanning-configuration scanOnPush=true \
--query 'repository.repositoryUri' \
--output text \
)
aws ecr get-login-password | docker login --username AWS --password-stdin ${repo_uri}
docker tag ${app_name}:latest ${repo_uri}:${app_version}
docker push ${repo_uri}:${app_version}
for letter in {a..z}; do
kubectl create deployment ${app_name}-${letter} --image ${repo_uri}:${app_version} -n ${app_name} -o yaml --dry-run=true > ~/environment/${app_name}/manifests/${app_name}-${letter}.yaml
echo "---" >> ~/environment/${app_name}/manifests/${app_name}-${letter}.yaml
kubectl create service nodeport ${app_name}-${letter} --tcp=80 -n ${app_name} -o yaml --dry-run=true >> ~/environment/${app_name}/manifests/${app_name}-${letter}.yaml
done
cat > ~/environment/${app_name}/manifests/alb.yaml << EOF
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ${app_name}
namespace: ${app_name}
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
spec:
rules:
- http:
paths:
EOF
for letter in {a..z}; do
cat >> ~/environment/${app_name}/manifests/alb.yaml << EOF
- path: /${letter}*
backend:
serviceName: alpha-${letter}
servicePort: 80
EOF
done
# TODO will i really need one virtual node per service before the webhook will work?
kubectl create namespace ${app_name}
# kubectl label namespace ${app_name} mesh=${app_name}
# kubectl label namespace ${app_name} appmesh.k8s.aws/sidecarInjectorWebhook=enabled
kubectl apply -f ~/environment/${app_name}/manifests/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment