|
| 1 | +# Deploy Data IO App to AWS EKS and Integrate with Kong Data Plane |
| 2 | + |
| 3 | +This runbook deploys your local app folder to EKS and routes traffic through Kong Data Plane. |
| 4 | + |
| 5 | +## Scope |
| 6 | + |
| 7 | +This guide assumes: |
| 8 | +- EKS cluster and Kong Data Plane are already running from earlier docs. |
| 9 | +- Kong Identity is already configured. |
| 10 | +- Your app no longer exposes local oauth endpoints and only serves Data IO actions. |
| 11 | + |
| 12 | +Related docs in this repo: |
| 13 | +- ../1. AWS-EKS/aws-eks.md |
| 14 | +- ../2. Kong/kong.md |
| 15 | +- ./kong_identity.md |
| 16 | + |
| 17 | +## 1. Create the DynamoDB tables |
| 18 | + |
| 19 | +The app uses DynamoDB as its database. Create the required tables and grant the EKS pods access to them via IRSA (IAM Roles for Service Accounts) before deploying the app image. |
| 20 | + |
| 21 | +#### Create the tables |
| 22 | + |
| 23 | +The app expects three DynamoDB tables with these partition keys: |
| 24 | +- `Accounts` with `account_id` (string) |
| 25 | +- `Addresses` with `address_id` (string) |
| 26 | + |
| 27 | +```bash |
| 28 | +aws dynamodb create-table \ |
| 29 | + --table-name Accounts \ |
| 30 | + --attribute-definitions AttributeName=account_id,AttributeType=S \ |
| 31 | + --key-schema AttributeName=account_id,KeyType=HASH \ |
| 32 | + --billing-mode PAY_PER_REQUEST \ |
| 33 | + --region "$AWS_REGION" |
| 34 | + |
| 35 | +aws dynamodb create-table \ |
| 36 | + --table-name Addresses \ |
| 37 | + --attribute-definitions AttributeName=address_id,AttributeType=S \ |
| 38 | + --key-schema AttributeName=address_id,KeyType=HASH \ |
| 39 | + --billing-mode PAY_PER_REQUEST \ |
| 40 | + --region "$AWS_REGION" |
| 41 | + |
| 42 | +Wait until all tables are active: |
| 43 | + |
| 44 | +```bash |
| 45 | +aws dynamodb wait table-exists --table-name Accounts --region "$AWS_REGION" |
| 46 | +aws dynamodb wait table-exists --table-name Addresses --region "$AWS_REGION" |
| 47 | +
|
| 48 | +aws dynamodb describe-table --table-name Accounts --region "$AWS_REGION" \ |
| 49 | + --query "Table.TableStatus" |
| 50 | +aws dynamodb describe-table --table-name Addresses --region "$AWS_REGION" \ |
| 51 | + --query "Table.TableStatus" |
| 52 | +``` |
| 53 | + |
| 54 | +You should see: |
| 55 | + |
| 56 | +``` |
| 57 | +"ACTIVE" |
| 58 | +``` |
| 59 | + |
| 60 | +#### Create an IAM policy for DynamoDB access |
| 61 | + |
| 62 | +```bash |
| 63 | +aws iam create-policy \ |
| 64 | + --policy-name DocusignDataIODynamoDBPolicy \ |
| 65 | + --policy-document '{ |
| 66 | + "Version": "2012-10-17", |
| 67 | + "Statement": [{ |
| 68 | + "Effect": "Allow", |
| 69 | + "Action": [ |
| 70 | + "dynamodb:GetItem", |
| 71 | + "dynamodb:PutItem", |
| 72 | + "dynamodb:UpdateItem", |
| 73 | + "dynamodb:DeleteItem", |
| 74 | + "dynamodb:Query", |
| 75 | + "dynamodb:Scan" |
| 76 | + ], |
| 77 | + "Resource": [ |
| 78 | + "arn:aws:dynamodb:'"$AWS_REGION"':'"$AWS_ACCOUNT_ID"':table/Accounts", |
| 79 | + "arn:aws:dynamodb:'"$AWS_REGION"':'"$AWS_ACCOUNT_ID"':table/Addresses", |
| 80 | + ] |
| 81 | + }] |
| 82 | + }' |
| 83 | +``` |
| 84 | + |
| 85 | +#### Create an IRSA role and bind it to the app service account |
| 86 | + |
| 87 | +This allows the pods to access DynamoDB without embedding credentials. |
| 88 | + |
| 89 | +First, ensure the cluster has an IAM OIDC provider associated (required for IRSA): |
| 90 | + |
| 91 | +```bash |
| 92 | +eksctl utils associate-iam-oidc-provider --region "$AWS_REGION" --cluster kong313 --approve |
| 93 | +``` |
| 94 | + |
| 95 | +Then create the service account: |
| 96 | + |
| 97 | +```bash |
| 98 | +eksctl create iamserviceaccount \ |
| 99 | + --cluster kong313 \ |
| 100 | + --region "$AWS_REGION" \ |
| 101 | + --namespace kong-apps \ |
| 102 | + --name docusign-dataio \ |
| 103 | + --attach-policy-arn "arn:aws:iam::${AWS_ACCOUNT_ID}:policy/DocusignDataIODynamoDBPolicy" \ |
| 104 | + --approve \ |
| 105 | + --override-existing-serviceaccounts |
| 106 | +``` |
| 107 | + |
| 108 | +#### Verify the service account annotation |
| 109 | + |
| 110 | +```bash |
| 111 | +kubectl get serviceaccount docusign-dataio -n kong-apps -o jsonpath='{.metadata.annotations}' | jq |
| 112 | +``` |
| 113 | + |
| 114 | +You should see an `eks.amazonaws.com/role-arn` annotation pointing to the newly created IAM role. |
| 115 | + |
| 116 | +## 2. Build and push the app image to ECR |
| 117 | + |
| 118 | +Run from repo root. |
| 119 | + |
| 120 | +```bash |
| 121 | +export AWS_REGION=us-west-2 |
| 122 | +export AWS_ACCOUNT_ID=<YOUR_ACCOUNT_ID> |
| 123 | +export ECR_REPO=docusign-dataio |
| 124 | +
|
| 125 | +aws ecr describe-repositories --repository-names "$ECR_REPO" --region "$AWS_REGION" >/dev/null 2>&1 || \ |
| 126 | +aws ecr create-repository --repository-name "$ECR_REPO" --region "$AWS_REGION" |
| 127 | +
|
| 128 | +aws ecr get-login-password --region "$AWS_REGION" | \ |
| 129 | +docker login --username AWS --password-stdin "$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com" |
| 130 | +
|
| 131 | +# --platform linux/amd64 is required when building on Apple Silicon (M1/M2/M3) |
| 132 | +# to ensure the image runs on x86_64 EKS nodes |
| 133 | +docker build --platform linux/amd64 -t "$ECR_REPO:latest" ./app |
| 134 | +
|
| 135 | +docker tag "$ECR_REPO:latest" "$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPO:latest" |
| 136 | +docker push "$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPO:latest" |
| 137 | +``` |
| 138 | + |
| 139 | +To redeploy after pushing an updated image, trigger a rolling restart: |
| 140 | + |
| 141 | +```bash |
| 142 | +kubectl -n kong-apps rollout restart deploy/docusign-dataio |
| 143 | +kubectl -n kong-apps rollout status deploy/docusign-dataio |
| 144 | +``` |
| 145 | + |
| 146 | +## 3. Deploy the app to EKS |
| 147 | + |
| 148 | +Create namespace and deployment. |
| 149 | + |
| 150 | +```bash |
| 151 | +cat <<EOF | kubectl apply -f - |
| 152 | +apiVersion: v1 |
| 153 | +kind: Namespace |
| 154 | +metadata: |
| 155 | + name: kong-apps |
| 156 | +--- |
| 157 | +apiVersion: apps/v1 |
| 158 | +kind: Deployment |
| 159 | +metadata: |
| 160 | + name: docusign-dataio |
| 161 | + namespace: kong-apps |
| 162 | +spec: |
| 163 | + replicas: 2 |
| 164 | + selector: |
| 165 | + matchLabels: |
| 166 | + app: docusign-dataio |
| 167 | + template: |
| 168 | + metadata: |
| 169 | + labels: |
| 170 | + app: docusign-dataio |
| 171 | + spec: |
| 172 | + serviceAccountName: docusign-dataio |
| 173 | + containers: |
| 174 | + - name: app |
| 175 | + image: $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPO:latest |
| 176 | + imagePullPolicy: Always |
| 177 | + env: |
| 178 | + - name: NODE_ENV |
| 179 | + value: production |
| 180 | + - name: PORT |
| 181 | + value: "3000" |
| 182 | + - name: AWS_REGION |
| 183 | + value: "$AWS_REGION" |
| 184 | + - name: DYNAMODB_TABLE_ACCOUNTS |
| 185 | + value: "Accounts" |
| 186 | + - name: DYNAMODB_TABLE_ADDRESSES |
| 187 | + value: "Addresses" |
| 188 | + ports: |
| 189 | + - containerPort: 3000 |
| 190 | + readinessProbe: |
| 191 | + tcpSocket: |
| 192 | + port: 3000 |
| 193 | + initialDelaySeconds: 10 |
| 194 | + periodSeconds: 10 |
| 195 | + livenessProbe: |
| 196 | + tcpSocket: |
| 197 | + port: 3000 |
| 198 | + initialDelaySeconds: 20 |
| 199 | + periodSeconds: 20 |
| 200 | +--- |
| 201 | +apiVersion: v1 |
| 202 | +kind: Service |
| 203 | +metadata: |
| 204 | + name: docusign-dataio |
| 205 | + namespace: kong-apps |
| 206 | +spec: |
| 207 | + type: ClusterIP |
| 208 | + selector: |
| 209 | + app: docusign-dataio |
| 210 | + ports: |
| 211 | + - name: http |
| 212 | + port: 3000 |
| 213 | + targetPort: 3000 |
| 214 | +EOF |
| 215 | +``` |
| 216 | + |
| 217 | +Check rollout. |
| 218 | + |
| 219 | +```bash |
| 220 | +kubectl -n kong-apps rollout status deploy/docusign-dataio |
| 221 | +kubectl -n kong-apps get pods -l app=docusign-dataio |
| 222 | +kubectl -n kong-apps get svc docusign-dataio |
| 223 | +``` |
| 224 | + |
| 225 | +## 4. Configure Kong to route to the app service (remove mock plugin) |
| 226 | + |
| 227 | +Create a new decK file for production routing. |
| 228 | + |
| 229 | +```bash |
| 230 | +cat <<'EOF' > kong_docusign_openid_upstream.yaml |
| 231 | +_format_version: "3.0" |
| 232 | +services: |
| 233 | +- name: docusign-dataio |
| 234 | + url: http://docusign-dataio.kong-apps.svc.cluster.local:3000 |
| 235 | + routes: |
| 236 | + - name: docusign-dataio-route |
| 237 | + paths: |
| 238 | + - / |
| 239 | + strip_path: false |
| 240 | + plugins: |
| 241 | + - name: openid-connect |
| 242 | + instance_name: openid-connect-docusign-kong-identity |
| 243 | + enabled: true |
| 244 | + config: |
| 245 | + issuer: ${{ env "DECK_ISSUER" }} |
| 246 | + client_id: |
| 247 | + - ${{ env "DECK_CLIENT_ID" }} |
| 248 | + client_secret: |
| 249 | + - ${{ env "DECK_CLIENT_SECRET" }} |
| 250 | + auth_methods: |
| 251 | + - introspection |
| 252 | + introspection_endpoint: ${{ env "DECK_KONG_IDENTITY_INTROSPECTION_URL" }} |
| 253 | +EOF |
| 254 | +``` |
| 255 | + |
| 256 | +Set env vars and sync config. |
| 257 | + |
| 258 | +```bash |
| 259 | +export DECK_ISSUER=$ISSUER_URL |
| 260 | +export DECK_KONG_IDENTITY_INTROSPECTION_URL=$ISSUER_URL/introspect |
| 261 | +export DECK_CLIENT_ID=$CLIENT_ID |
| 262 | +export DECK_CLIENT_SECRET=$CLIENT_SECRET |
| 263 | +
|
| 264 | +deck gateway reset --konnect-control-plane-name kong-aws --konnect-token "$PAT" -f |
| 265 | +deck gateway sync --konnect-control-plane-name kong-aws --konnect-token "$PAT" kong_docusign_openid_upstream.yaml |
| 266 | +``` |
| 267 | + |
| 268 | +## 5. Update manifest auth endpoints to Kong Identity |
| 269 | + |
| 270 | +Edit app/manifest.json. |
| 271 | + |
| 272 | +Set authentication connection customConfig values to Kong Identity, not your app: |
| 273 | +- tokenUrl: $ISSUER_URL/oauth/token |
| 274 | +- authorizationUrl: $ISSUER_URL/oauth/authorize |
| 275 | + |
| 276 | +Keep action URIs pointing to Kong Data Plane: |
| 277 | +- https://kong-dp.<your-domain>/api/dataio/createRecord |
| 278 | +- https://kong-dp.<your-domain>/api/dataio/patchRecord |
| 279 | +- https://kong-dp.<your-domain>/api/dataio/searchRecords |
| 280 | +- https://kong-dp.<your-domain>/api/dataio/getTypeNames |
| 281 | +- https://kong-dp.<your-domain>/api/dataio/getTypeDefinitions |
| 282 | + |
| 283 | +## 6. Validate through Kong Data Plane |
| 284 | + |
| 285 | +```bash |
| 286 | +export DATA_PLANE_LB=kong-dp.$AWS_DOMAIN |
| 287 | +
|
| 288 | +curl -s -X POST "https://$DATA_PLANE_LB/api/dataio/getTypeNames" | jq |
| 289 | +curl -s -X POST "https://$DATA_PLANE_LB/api/dataio/getTypeDefinitions" | jq |
| 290 | +``` |
| 291 | + |
| 292 | +Then run Extension App tests in the Docusign Developer Console. |
| 293 | + |
| 294 | +## 7. Rollback if needed |
| 295 | + |
| 296 | +If you need to temporarily return to mocks: |
| 297 | + |
| 298 | +```bash |
| 299 | +deck gateway sync --konnect-control-plane-name kong-aws --konnect-token "$PAT" kong_docusign_openid_mock.yaml |
| 300 | +``` |
| 301 | + |
| 302 | +## Notes |
| 303 | + |
| 304 | +- This app now depends on Kong for auth enforcement. |
| 305 | +- Keep the app Service internal to the cluster. |
| 306 | +- DynamoDB access is granted via IRSA — no credentials are embedded in the deployment. |
0 commit comments