Deploy your first FaaS Application on Knative
2020年12月26日
Grant permissions to the service account. Replace [ProjectId] with your project ID and ROLE with the appropriate role for the service account.
$ gcloud projects add-iam-policy-binding [ProjectId] --member "serviceAccount:SvcAccName@###.iam.gserviceaccount.com" --role "roles/viewer"
Generate the key file. In this example, the output key file name is keyfile.json.
$ gcloud iam service-accounts keys create keyfile.json --iam-account SvcAccName@[ProjectId].iam.gserviceaccount.com
Use the service account key as your password to authenticate with Docker.
$ cat keyfile.json | docker login -u _json_key --password-stdin https://gcr.io
> --docker-server=https://gcr.io \
> --docker-username=_json_key \
> --docker-password="$(cat ./keyfile.json)"
$ kubectl get secret
Clone the repo into a new directory. In this example, we name the new directory as "blog-knative".
$ git clone -b master git@github.com:###/###.git blog-knative
$ cd blog-knative/
The example "MyTableName" represents the DynamoDB table name. The "MyIndexName" represents the index of this DynamoDB table.
$ cat blog-knative/env_test.sh
Use your existing GCP project ID. Replace <GCP-Project-ID> with your GCP project ID.
export GCPPROJECT=<GCP-Project-ID>
export REPO="gcr.io/${GCPPROJECT}"
export TAG="${REPO}/knative-pk2hash" IMAGE=$TAG
Replace <DynamoDBTableName> with your DynamoDB table name.
export TableName=<DynamoDBTableName>
Replace <DynamoDBIndexName> with your DynamoDB table's index name.
export IndexName=<DynamoDBIndexName>
$ source blog-knative/env_test.sh
Prebake an container image for futher shared use. File "DockerfilePrebakeImg":
Below WORKDIR instruction makes sure when the container runs, inside the container, the current directory is "/app/". If the specific directory does not exist in the original image, the WORKDIR will create this new directory automatically.
$ docker build --tag "${REPO}/prebake" --file DockerfilePrebakeImg .
$ docker push "${REPO}/prebake"
Dockerfile:
$ docker build --tag $TAG --file pk2hash/Dockerfile .
When checking the content of this directory (e.g. access it via: docker run -it "${REPO}/rest-api-go"), we could observe that files are copied from the outside directory (at which directory that Dockerfile exist) to the inside container directory, i.e. /app/.
Below command in the Dockerfile is will build out a new file called "main" under the directory "/app/".
Push the container image to a container registry.
$ docker push $TAG
$ cat sample-template.yaml
$ envsubst < pk2hash/sample-template.yaml > pk2hash/pkhash.yaml
On the machine running kubectl client, issue below command.
Edit aws-auth ConfigMap. Replace <user> with your IAM user name. Use "aws sts get-caller-identity" command to get the current IAM user's ARN.
$ kubectl apply -f pk2hash/pkhash.yaml
$ watch kubectl get pod
NB
Make sure the frontend application code is calls URL: ###.default.tianzhui.cloud
Test:
test/test_test.sh:
$ ./pk2hash/test/test_test.sh
Tear down the test environment.
$ kubectl delete -f pk2hash/pkhash.yaml
env_prod.sh:
$ source pk2hash/env_prod.sh
$ docker tag gcr.io/[ProjectId]/knative-pk2hash:test gcr.io/[ProjectId]/knative-pk2hash:prod
Push the image to the "prod" tag.
$ docker push $TAG
$ envsubst < pk2hash/sample-template.yaml > pk2hash/pkhash.yaml
Apply the configuration to the production environment.
$ kubectl apply -f pk2hash/pkhash.yaml
Test:
test/test_prod.sh:
$ ./pk2hash/test/test_prod.sh
$ kubectl get ksvc --output=custom-columns=NAME:.metadata.name,URL:.status.url
$ curl http://pk2hash.default.tianzhui.cloud/prod/hasher?querystring=1##9,3##1
References
How to install & setup Gettext
Knative code samples
Knative Serving code samples
Creating a RESTful Service - Go
knative / docs release 0.15
How do I resolve an unauthorized server error when I connect to the Amazon EKS API server?
JSON key file
Using Google Container Registry with Kubernetes
In GCP, create a new service account and a service account key for use with Container Registry repositories only.
Create the service account for interacting with repositories. Replace SvcAccName with a name for the service account.
$ gcloud iam service-accounts create SvcAccName
Created service account [SvcAccName].
Grant permissions to the service account. Replace [ProjectId] with your project ID and ROLE with the appropriate role for the service account.
$ gcloud projects add-iam-policy-binding [ProjectId] --member "serviceAccount:SvcAccName@###.iam.gserviceaccount.com" --role "roles/viewer"
Updated IAM policy for project [###]. bindings: ... - members: - serviceAccount:SvcAccName@###.iam.gserviceaccount.com role: roles/viewer etag: BwW39TyIHMo= version: 1
Generate the key file. In this example, the output key file name is keyfile.json.
$ gcloud iam service-accounts keys create keyfile.json --iam-account SvcAccName@[ProjectId].iam.gserviceaccount.com
created key [1291###6612] of type [json] as [keyfile.json] for [SvcAccName@###.iam.gserviceaccount.com]
Use the service account key as your password to authenticate with Docker.
$ cat keyfile.json | docker login -u _json_key --password-stdin https://gcr.io
Login SucceededDocker is now authenticated with Container Registry.
kubectl create secret docker-registry gcr-json-key \ --docker-server=https://gcr.io \ --docker-username=_json_key \ --docker-password="$(cat ./keyfile.json)"$ kubectl create secret docker-registry gcr-json-key \
> --docker-server=https://gcr.io \
> --docker-username=_json_key \
> --docker-password="$(cat ./keyfile.json)"
secret/gcr-json-key created
$ kubectl get secret
NAME TYPE DATA AGE ... gcr-json-key kubernetes.io/dockerconfigjson 1 45d ...
spec: ... template: ... spec: imagePullSecrets: - name: gcr-json-key containers: - image: ... ...
Clone the repo into a new directory. In this example, we name the new directory as "blog-knative".
$ git clone -b master git@github.com:###/###.git blog-knative
Cloning into 'blog-knative'... remote: Enumerating objects: 35, done. remote: Counting objects: 100% (35/35), done. remote: Compressing objects: 100% (29/29), done. remote: Total 35 (delta 10), reused 30 (delta 5), pack-reused 0 Receiving objects: 100% (35/35), 13.50 KiB | 13.50 MiB/s, done. Resolving deltas: 100% (10/10), done.
$ cd blog-knative/
The example "MyTableName" represents the DynamoDB table name. The "MyIndexName" represents the index of this DynamoDB table.
$ cat blog-knative/env_test.sh
#!/bin/bash # To get var persist after this script has completed: # source ./env_test.sh export GCPPROJECT=### export REPO="gcr.io/${GCPPROJECT}" export TAG="${REPO}/knative-pk2hash" IMAGE=$TAG export TableName=MyTableName export IndexName=MyIndexName
Use your existing GCP project ID. Replace <GCP-Project-ID> with your GCP project ID.
export GCPPROJECT=<GCP-Project-ID>
export REPO="gcr.io/${GCPPROJECT}"
export TAG="${REPO}/knative-pk2hash" IMAGE=$TAG
Replace <DynamoDBTableName> with your DynamoDB table name.
export TableName=<DynamoDBTableName>
Replace <DynamoDBIndexName> with your DynamoDB table's index name.
export IndexName=<DynamoDBIndexName>
$ source blog-knative/env_test.sh
Prebake an container image for futher shared use. File "DockerfilePrebakeImg":
FROM golang WORKDIR /app RUN go get github.com/aws/aws-lambda-go/events RUN go get github.com/aws/aws-lambda-go/lambda RUN go get github.com/aws/aws-sdk-go/aws RUN go get github.com/aws/aws-sdk-go/aws/awserr RUN go get github.com/aws/aws-sdk-go/aws/session RUN go get github.com/aws/aws-sdk-go/service/dynamodb RUN go get github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute RUN go get github.com/gorilla/muxPS:
Below WORKDIR instruction makes sure when the container runs, inside the container, the current directory is "/app/". If the specific directory does not exist in the original image, the WORKDIR will create this new directory automatically.
... WORKDIR /app ...The packages will be installed to directory "/go/src/github.com/".
... RUN go get github.com/aws/aws-lambda-go/events RUN go get github.com/aws/aws-lambda-go/lambda RUN go get github.com/aws/aws-sdk-go/aws RUN go get github.com/aws/aws-sdk-go/aws/awserr RUN go get github.com/aws/aws-sdk-go/aws/session RUN go get github.com/aws/aws-sdk-go/service/dynamodb RUN go get github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute RUN go get github.com/gorilla/mux ...
$ docker build --tag "${REPO}/prebake" --file DockerfilePrebakeImg .
Sending build context to Docker daemon 97.28kB Step 1/10 : FROM golang ---> 5f9d35ce5cfe Step 2/10 : WORKDIR /app ---> Using cache ---> 64e0befeef58 Step 3/10 : RUN go get github.com/aws/aws-lambda-go/events ---> Using cache ---> dfb44cbcd7e7 Step 4/10 : RUN go get github.com/aws/aws-lambda-go/lambda ---> Using cache ---> c7cf0bdb4c79 Step 5/10 : RUN go get github.com/aws/aws-sdk-go/aws ---> Using cache ---> 9de8d250af45 Step 6/10 : RUN go get github.com/aws/aws-sdk-go/aws/awserr ---> Using cache ---> 68b5a9290717 Step 7/10 : RUN go get github.com/aws/aws-sdk-go/aws/session ---> Using cache ---> c28beb73e648 Step 8/10 : RUN go get github.com/aws/aws-sdk-go/service/dynamodb ---> Using cache ---> 86aeb0da3aa8 Step 9/10 : RUN go get github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute ---> Using cache ---> cd413d583eef Step 10/10 : RUN go get github.com/gorilla/mux ---> Using cache ---> 92bd4f601663 Successfully built 92bd4f601663 Successfully tagged gcr.io/[GCP-Project-ID]/prebake:latest
$ docker push "${REPO}/prebake"
Using default tag: latest The push refers to repository [gcr.io/[GCP-Project-ID]/prebake] fa03def44984: Pushed 8e8b467b9722: Pushed 3fc1d702cf24: Pushed 65c457f81b03: Pushed d895236ab75c: Pushed cb585322a308: Pushed 6f498ce811ae: Pushed 15d4d54598fb: Pushed 73465b7c2cdd: Pushed 86eac4a15979: Layer already exists 4e68eae3a5f4: Layer already exists 7857d5f3d252: Layer already exists c5f4367d4a59: Layer already exists ceecb62b2fcc: Layer already exists 193bc1d68b80: Layer already exists f0e10b20de19: Layer already exists latest: digest: sha256:36f0e20f2145ead31cd7dc26e14cc68cd88cc12f34b1fa8ab6c008ccee46b397 size: 3686
Dockerfile:
FROM gcr.io/[ProjectId]/prebake AS builder COPY ./pk2hash/src /app/ # Below build task will generate a file called "main" in the "/app/" directory # inside the container image. RUN CGO_ENABLED=0 go build -o main . FROM gcr.io/distroless/base COPY --from=builder /app/main /sample ENTRYPOINT ["/sample"]
$ docker build --tag $TAG --file pk2hash/Dockerfile .
Sending build context to Docker daemon 239.6kB Step 1/6 : FROM gcr.io/[GCP-Project-ID]/prebake AS builder ---> 04793abeda00 Step 2/6 : COPY ./src /app/ ---> 98d0c08f37f7 Step 3/6 : RUN CGO_ENABLED=0 go build -o main . ---> Running in b3136db4af3d Removing intermediate container b3136db4af3d ---> 0a9dadbfc624 Step 4/6 : FROM gcr.io/distroless/base ---> 5cda88510683 Step 5/6 : COPY --from=builder /app/main /sample ---> Using cache ---> 562526b3d327 Step 6/6 : ENTRYPOINT ["/sample"] ---> Using cache ---> bdc5c0356d23 Successfully built bdc5c0356d23 Successfully tagged gcr.io/[GCP-Project-ID]/knative-pk2hash:test
When checking the content of this directory (e.g. access it via: docker run -it "${REPO}/rest-api-go"), we could observe that files are copied from the outside directory (at which directory that Dockerfile exist) to the inside container directory, i.e. /app/.
root@###:/app# ls -lah /app/ total 7.3M drwxr-xr-x 1 root root 4.0K Dec 28 23:23 . drwxr-xr-x 1 root root 4.0K Dec 28 14:01 .. drwxr-xr-x 8 root root 4.0K Dec 28 23:20 .git drwxr-xr-x 3 root root 4.0K Dec 28 23:20 .github -rw-r--r-- 1 root root 35 Dec 28 23:20 .gitignore -rw-r--r-- 1 root root 19K Dec 28 23:20 CONTRIBUTING.md -rw-r--r-- 1 root root 15K Dec 28 23:20 Gopkg.lock -rw-r--r-- 1 root root 433 Dec 28 23:20 Gopkg.toml -rw-r--r-- 1 root root 30K Dec 28 23:20 LICENSE -rw-r--r-- 1 root root 161 Dec 28 23:20 OWNERS -rw-r--r-- 1 root root 1.1K Dec 28 23:20 OWNERS_ALIASES -rw-r--r-- 1 root root 3.5K Dec 28 23:20 README.md -rw-r--r-- 1 root root 5.1K Dec 28 23:20 _index.html -rw-r--r-- 1 root root 21K Dec 28 23:20 background.png drwxr-xr-x 7 root root 4.0K Dec 28 23:20 blog drwxr-xr-x 6 root root 4.0K Dec 28 23:20 community -rw-r--r-- 1 root root 838 Dec 28 23:20 doc-releases.md drwxr-xr-x 9 root root 4.0K Dec 28 23:20 docs drwxr-xr-x 2 root root 4.0K Dec 28 23:20 hack -rw-r--r-- 1 root root 3.2K Dec 28 23:20 new-page-template.md -rwxr-xr-x 1 root root 7.2M Dec 28 23:23 rest-api-go -rw-r--r-- 1 root root 45 Dec 28 23:20 search.md -rw-r--r-- 1 root root 2.9K Dec 28 23:20 smoketest.md drwxr-xr-x 6 root root 4.0K Dec 28 23:20 test drwxr-xr-x 10 root root 4.0K Dec 28 23:20 vendor
Below command in the Dockerfile is will build out a new file called "main" under the directory "/app/".
RUN CGO_ENABLED=0 go build -o main .
Push the container image to a container registry.
$ docker push $TAG
The push refers to repository [gcr.io/[GCP-Project-ID]/knative-pk2hash] 1ef81aba1286: Pushed e2db5f1bf240: Layer already exists 7a5b9c0b4b14: Layer already exists latest: digest: sha256:8a1e8429b1b2263994a0039ea0b280d27f3ff7eed7b70d9914a686558ca918bc size: 949
$ cat sample-template.yaml
apiVersion: serving.knative.dev/v1 kind: Service metadata: name: pk2hash namespace: default spec: template: metadata: name: pk2hash-first annotations: sidecar.istio.io/rewriteAppHTTPProbers: "true" spec: containers: - image: ${IMAGE} imagePullPolicy: Always env: - name: TableName value: ${TableName} - name: IndexName value: ${IndexName} readinessProbe: httpGet: path: /healthcheck initialDelaySeconds: 5 periodSeconds: 30 timeoutSeconds: 10 failureThreshold: 3 livenessProbe: httpGet: path: /healthcheck initialDelaySeconds: 5 periodSeconds: 30 timeoutSeconds: 10 failureThreshold: 3Execute below command, which will replace the image name, table name and index name based on the environment variables.
$ envsubst < pk2hash/sample-template.yaml > pk2hash/pkhash.yaml
On the machine running kubectl client, issue below command.
Edit aws-auth ConfigMap. Replace <user> with your IAM user name. Use "aws sts get-caller-identity" command to get the current IAM user's ARN.
apiVersion: v1 kind: ConfigMap metadata: name: aws-auth namespace: kube-system data: mapRoles: | ... mapUsers: | - userarn: arn:aws:iam::123456789012:user/<user> username: <user> groups: - system:masters
$ kubectl apply -f pk2hash/pkhash.yaml
service.serving.knative.dev/### created
$ watch kubectl get pod
NB
Make sure the frontend application code is calls URL: ###.default.tianzhui.cloud
Test:
test/test_test.sh:
#!/bin/bash echo "Verifying Health Check API... (no news is good news)" curl "http://pk2hash-test.default.tianzhui.cloud/healthcheck" echo "Testing API..." echo "Test result:" curl "http://pk2hash-test.default.tianzhui.cloud/prod/hasher?querystring=1##9,3##1" echo "" echo "Test completed."
$ ./pk2hash/test/test_test.sh
Verifying Health Check API... (no news is good news) Testing API... Test result: {"id_results":{"10kk4jvq":"1##9","15v2dcnv":"3##1"}} Test completed.
Tear down the test environment.
$ kubectl delete -f pk2hash/pkhash.yaml
service.serving.knative.dev "pk2hash-test" deleted
env_prod.sh:
#!/bin/bash # To get var persist after this script has completed: # source ./env_prod.sh export GCPPROJECT=ProjectId export REPO="gcr.io/${GCPPROJECT}" export ENV="prod" export TAG="${REPO}/knative-pk2hash:${ENV}" export IMAGE=$TAG export TableName=MyTableName export IndexName=MyIndexName
$ source pk2hash/env_prod.sh
$ docker tag gcr.io/[ProjectId]/knative-pk2hash:test gcr.io/[ProjectId]/knative-pk2hash:prod
Push the image to the "prod" tag.
$ docker push $TAG
The push refers to repository [gcr.io/[ProjectId]/knative-pk2hash] e###3: Layer already exists e###0: Layer already exists 7###4: Layer already exists prod: digest: sha256:5###b size: 949
$ envsubst < pk2hash/sample-template.yaml > pk2hash/pkhash.yaml
Apply the configuration to the production environment.
$ kubectl apply -f pk2hash/pkhash.yaml
Test:
test/test_prod.sh:
#!/bin/bash echo "Verifying Health Check API... (no news is good news)" curl "http://pk2hash-prod.default.tianzhui.cloud/healthcheck" echo "Testing API..." echo "Test result:" curl "http://pk2hash-prod.default.tianzhui.cloud/prod/hasher?querystring=1##9,3##1" echo "" echo "Test completed."
$ ./pk2hash/test/test_prod.sh
Verifying Health Check API... (no news is good news) Testing API... Test result: {"id_results":{"10kk4jvq":"1##9","15v2dcnv":"3##1"}} Test completed.
$ kubectl get ksvc --output=custom-columns=NAME:.metadata.name,URL:.status.url
NAME URL pk2hash http://pk2hash.default.tianzhui.cloud
$ curl http://pk2hash.default.tianzhui.cloud/prod/hasher?querystring=1##9,3##1
{"id_results":{"1###q":"1##9","1###v":"3##1"}}
References
How to install & setup Gettext
Knative code samples
Knative Serving code samples
Creating a RESTful Service - Go
knative / docs release 0.15
How do I resolve an unauthorized server error when I connect to the Amazon EKS API server?
JSON key file
Using Google Container Registry with Kubernetes
-