What should be the host name in nestjs hybrid microservice when deployed on Kubernetes - kubernetes

Tech stack -
nestjs - 2 microservice
kubernetes - AWS EKS
Ingress - nginx
Hybrid
const app = await NestFactory.create(AppModule);
const microservice = app.connectMicroservice<MicroserviceOptions>(
{
transport: Transport.TCP,
options: {
host: process.env.TCP_HOST,
port: parseInt(process.env.TCP_EVALUATION_PORT),
},
},
{ inheritAppConfig: true },
);
await app.startAllMicroservices();
await app.listen(parseInt(config.get(ConfigEnum.PORT)), '0.0.0.0');
env
TCP_HOST: '0.0.0.0'
TCP_CORE_PORT: 8080
TCP_EVALUATION_PORT: 8080
Error
"connect ECONNREFUSED 0.0.0.0:8080"
Do I need to expose this port in docker or add it somewhere in the security group?
Or may be need to pass a different host?
Note: App is deployed properly without any error and HTTP Rest API seems to be working fine but not the TCP #messagePattern!
Thanks

Create a service to match the instances you want to connect to and use the service name.

Basically in Hybrid application, main.ts configuration will be like below -
service1
const app = await NestFactory.create(AppModule);
const microservice = app.connectMicroservice<MicroserviceOptions>(
{
transport: Transport.TCP,
options: {
host: '0.0.0.0',
port: 6200,
},
},
{ inheritAppConfig: true },
);
await app.startAllMicroservices();
await app.listen(6300, '0.0.0.0');
In client
ClientsModule.register([
{
name: 'service1',
transport: Transport.TCP,
options: {
host: 'service1',
port: 6200,
},
},
]),

Related

Deploy a FargateService to an ECS that's living within a different Stack (preoject)

1- I have a project core-infra that encapasses all the core infra related compoents (VPCs, Subnets, ECS Cluster...etc)
2- I have microservice projects with independant stacks each used for deployment
I want to deploy a FargateService from a microservice project stack A to the already existing ECS living within the core-infra stack
Affected area/feature
Pulumi Service
ECS
Deploy microservice
FargateService
Pulumi github issue link
Pulumi Stack References are the answer here:
https://www.pulumi.com/docs/intro/concepts/stack/#stackreferences
Your core-infra stack would output the ECS cluster ID and then stack B consumes that output so it can, for example, deploy an ECS service to the given cluster
(https://www.pulumi.com/registry/packages/aws/api-docs/ecs/service/).
I was able to deploy using aws classic.
PS: The setup is way more complex than with the awsx, the doc and resources aren't exhaustive.
Now I have few issues:
The Loadbalancer isn't reachable and keeps loading forever
I don't have any logs in the CloudWatch LogGoup
Not sure how to use the LB Listner with the ECS service / Not sure about the port mapping
Here is the complete code for reference (people who're husteling) and I'd appreciate if you could suggest any improvments/answers.
// Capture the EnvVars
const appName = process.env.APP_NAME;
const namespace = process.env.NAMESPACE;
const environment = process.env.ENVIRONMENT;
// Load the Deployment Environment config.
const configMapLoader = new ConfigMapLoader(namespace, environment);
const env = pulumi.getStack();
const infra = new pulumi.StackReference(`org/core-datainfra/${env}`);
// Fetch ECS Fargate cluster ID.
const ecsClusterId = infra.getOutput('ecsClusterId');
// Fetch DeVpc ID.
const deVpcId = infra.getOutput('deVpcId');
// Fetch DeVpc subnets IDS.
const subnets = ['subnet-aaaaaaaaaa', 'subnet-bbbbbbbbb'];
// Fetch DeVpc Security Group ID.
const securityGroupId = infra.getOutput('deSecurityGroupId');
// Define the Networking for our service.
const serviceLb = new aws.lb.LoadBalancer(`${appName}-lb`, {
internal: false,
loadBalancerType: 'application',
securityGroups: [securityGroupId],
subnets,
enableDeletionProtection: false,
tags: {
Environment: environment
}
});
const serviceTargetGroup = new aws.lb.TargetGroup(`${appName}-t-g`, {
port: configMapLoader.configMap.service.http.externalPort,
protocol: configMapLoader.configMap.service.http.protocol,
vpcId: deVpcId,
targetType: 'ip'
});
const http = new aws.lb.Listener(`${appName}-listener`, {
loadBalancerArn: serviceLb.arn,
port: configMapLoader.configMap.service.http.externalPort,
protocol: configMapLoader.configMap.service.http.protocol,
defaultActions: [
{
type: 'forward',
targetGroupArn: serviceTargetGroup.arn
}
]
});
// Create AmazonECSTaskExecutionRolePolicy
const taskExecutionPolicy = new aws.iam.Policy(
`${appName}-task-execution-policy`,
{
policy: JSON.stringify({
Version: '2012-10-17',
Statement: [
{
Effect: 'Allow',
Action: [
'ecr:GetAuthorizationToken',
'ecr:BatchCheckLayerAvailability',
'ecr:GetDownloadUrlForLayer',
'ecr:BatchGetImage',
'logs:CreateLogStream',
'logs:PutLogEvents',
'ec2:AuthorizeSecurityGroupIngress',
'ec2:Describe*',
'elasticloadbalancing:DeregisterInstancesFromLoadBalancer',
'elasticloadbalancing:DeregisterTargets',
'elasticloadbalancing:Describe*',
'elasticloadbalancing:RegisterInstancesWithLoadBalancer',
'elasticloadbalancing:RegisterTargets'
],
Resource: '*'
}
]
})
}
);
// IAM role that allows Amazon ECS to make calls to the load balancer
const taskExecutionRole = new aws.iam.Role(`${appName}-task-execution-role`, {
assumeRolePolicy: JSON.stringify({
Version: '2012-10-17',
Statement: [
{
Effect: 'Allow',
Principal: {
Service: ['ecs-tasks.amazonaws.com']
},
Action: 'sts:AssumeRole'
},
{
Action: 'sts:AssumeRole',
Principal: {
Service: 'ecs.amazonaws.com'
},
Effect: 'Allow',
Sid: ''
},
{
Action: 'sts:AssumeRole',
Principal: {
Service: 'ec2.amazonaws.com'
},
Effect: 'Allow',
Sid: ''
}
]
}),
tags: {
name: `${appName}-iam-role`
}
});
new aws.iam.RolePolicyAttachment(`${appName}-role-policy`, {
role: taskExecutionRole.name,
policyArn: taskExecutionPolicy.arn
});
// New image to be pulled
const image = `${configMapLoader.configMap.service.image.repository}:${process.env.IMAGE_TAG}`;
// Set up Log Group
const awsLogGroup = new aws.cloudwatch.LogGroup(`${appName}-awslogs-group`, {
name: `${appName}-awslogs-group`,
tags: {
Application: `${appName}`,
Environment: 'production'
}
});
const serviceTaskDefinition = new aws.ecs.TaskDefinition(
`${appName}-task-definition`,
{
family: `${appName}-task-definition`,
networkMode: 'awsvpc',
executionRoleArn: taskExecutionRole.arn,
requiresCompatibilities: ['FARGATE'],
cpu: configMapLoader.configMap.service.resources.limits.cpu,
memory: configMapLoader.configMap.service.resources.limits.memory,
containerDefinitions: JSON.stringify([
{
name: `${appName}-fargate`,
image,
cpu: parseInt(
configMapLoader.configMap.service.resources.limits.cpu
),
memory: parseInt(
configMapLoader.configMap.service.resources.limits.memory
),
essential: true,
portMappings: [
{
containerPort: 80,
hostPort: 80
}
],
environment: configMapLoader.getConfigAsEnvironment(),
logConfiguration: {
logDriver: 'awslogs',
options: {
'awslogs-group': `${appName}-awslogs-group`,
'awslogs-region': 'us-east-2',
'awslogs-stream-prefix': `${appName}`
}
}
}
])
}
);
// Create a Fargate service task that can scale out.
const fargateService = new aws.ecs.Service(`${appName}-fargate`, {
name: `${appName}-fargate`,
cluster: ecsClusterId,
taskDefinition: serviceTaskDefinition.arn,
desiredCount: 5,
loadBalancers: [
{
targetGroupArn: serviceTargetGroup.arn,
containerName: `${appName}-fargate`,
containerPort: configMapLoader.configMap.service.http.internalPort
}
],
networkConfiguration: {
subnets
}
});
// Export the Fargate Service Info.
export const fargateServiceName = fargateService.name;
export const fargateServiceUrl = serviceLb.dnsName;
export const fargateServiceId = fargateService.id;
export const fargateServiceImage = image;

Heroku Postgres - The server does not support SSL connections

I have been working on an small app and connecting with Heroku PostgreSQL, for many days was working right but now is showing me this SSL error
The server does not support SSL connections
I have been looking for solutions but I cannot find anything that works for me, my code is:
import pg from 'pg'
import db from '../config.js'
const pool = new pg.Pool({
host: db.host,
database: db.database,
user: db.user,
port: db.port,
password: db.password,
ssl: { rejectUnauthorized: false },
})
export default function query(text, params) {
return pool.query(text, params)
}
Try changing it from Pool to Client and then using that to connect and query.
async function get(){
const client = new pg.Client({
ssl: {
rejectUnauthorized: false
},
user: ...,
password: ...,
port: ...,
host: ...,
database: ...
});
client.connect();
const response = await client.query(`SELECT * FROM ...;`)
return response.rows
}
Double check your values in the Heroku database.
Also, in production, you should just need
const client = new pg.Client({
connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: false
},
}
The first set of code is for local connection to your db.
One last note, I would put your user, password, etc... into a .env file and don't commit that to your repo.
UPDATE:
You can also put this into a config file like ./db.config.js as the following
const pg = require('pg')
module.exports =
process.env.DATABASE_URL
?
new pg.Client({
connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: false
},
})
:
new pg.Client({
// connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: false
},
user: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD,
port: process.env.DATABASE_PORT,
host: process.env.DATABASE_HOST,
database: process.env.DATABASE
})
So if it is in production, it will use the database url, and if there is none (which is local) then it will use the username password connection.

WebSocketException: Connection to url.. was not upgraded to websocket. It is working fine in angular but not in flutter why?

I have tried to do in 2 ways it's not working while it is working in angular
way
var channel = IOWebSocketChannel.connect(
Uri.parse('wss://......./...../mywebsockets'),
headers: {
'Connection': 'Upgrade',
'Upgrade': 'websocket'
}
);
channel.stream.listen((event) {
print(event);
});
2nd way
StompClient client = StompClient(
config: StompConfig.SockJS(
url: 'https://....../.../mywebsockets',
webSocketConnectHeaders: {
'Upgrade': 'websocket',
'Connection': 'Upgrade',
},
onConnect: (StompFrame connectFrame) {
print('connected');
},
onWebSocketError: (dynamic error) => print(error.toString()),
onStompError: (d) => print("stomp error"),
onDisconnect: (d)=> print("disconnect"),
)
);
At backend(java) we removed .withSockJs() it is not working. Removed handshake still not working.
The problem is that WebSocket source code is actually ditching the WSS scheme and sending an HTTPS request with websocket headers.
uri = Uri(
scheme: uri.isScheme("wss") ? "https" : "http",
userInfo: uri.userInfo,
host: uri.host,
port: uri.port,
path: uri.path,
query: uri.query,
fragment: uri.fragment);
Source: https://github.com/dart-lang/sdk/blob/aa793715e0f122cbd27cd8f9cc12201ff43c19b1/sdk/lib/_http/websocket_impl.dart#L1014 .

deno connect to mongodb

I've tried to follow this tutorial https://www.youtube.com/watch?v=VF38U2qd27Q, but to no avail.
I realised that the syntax in the video already obsolete for example connectWithUri to become connect.
but when I tried to connect to mongo using deno_mongo with the latest docs, it still not working.
import { MongoClient } from "https://deno.land/x/mongo#v0.20.1/mod.ts";
const dbString = `mongodb://${mongoUser}:${mongoPass}#${mongoHost}:${mongoPort}`;
const client = new MongoClient();
client.connect(dbString);
const db = client.database(mongoDB)
this.users = db.collection<UserSchema>("users");
Then I found another library denodb but again can't connect to mongodb:
import { Database } from 'https://deno.land/x/denodb/mod.ts';
const dbString = `mongodb://${mongoUser}:${mongoPass}#${mongoHost}:${mongoPort}`;
this.db = new Database('mongo', {
uri: dbString,
database: mongoDB
});
the error message:
error: Uncaught AssertionError
deno | throw new AssertionError(msg);
deno | ^
deno | at assert (asserts.ts:152:11)
deno | at MongoClient.database (client.ts:48:5)
deno | at new connectDB (connectDB.ts:35:23)
which part is wrong?
Looking at the deno_mongo README on GitHub.
For a local database you should use
//Connecting to a Local Database
await client.connect("mongodb://localhost:27017");
And if you are connecting to Mongo Atlas Database (and probably any other remote database) you should use:
//Connecting to a Mongo Atlas Database
await client.connect({
db: "<db_name>",
tls: true,
servers: [
{
host: "<db_cluster_url>",
port: 27017,
},
],
credential: {
username: "<username>",
password: "<password>",
db: "<db_name>",
mechanism: "SCRAM-SHA-1",
},
});
FYI
If you are using Mongo Atlas make sure to split up the connection string you get from 'Connection Wizard' in the Mongo Atlas dashboard over 3 (or how many replicas you have) entries in the server array. Like this:
servers: [
{
host: this.dbUrl1, // e.g. <name-of-cluster>-00-00.fbnrc.mongodb.net
port: 27017,
},
{
host: this.dbUrl2, // e.g. <name-of-cluster>-00-01.fbnrc.mongodb.net
port: 27017,
},
{
host: this.dbUrl3, // e.g. <name-of-cluster>-00-02.fbnrc.mongodb.net
port: 27017,
}
]
You get the connection string by:
Click 'Clusters' under Data storage (left side of the screen)
Click 'Connect' button
Click 'Connect to Application'
Select Driver: 'Node.js' and Version: '2.2.12 or later'
Connection string is displayed below. The servers are listed in a comma separated manner like this:
...<name-of-cluster>-00-00.fbnrc.mongodb.net:27017,<name-of-cluster>-00-01.fbnrc.mongodb.net:27017,<name-of-cluster>-00-02.fbnrc.mongodb.net:27017...
FYI-2
Make sure the master replica is listed first in the server array. Because if you want to do insertions into the database the master replica should be targeted. For me this was the 2nd mongo url, therefore the following server array worked for me:
servers: [
{
host: this.dbUrl2,
port: 27017,
},
{
host: this.dbUrl1,
port: 27017,
},
{
host: this.dbUrl3,
port: 27017,
}
]
the below code is working for me.
import { DataTypes, Database, Model } from 'https://deno.land/x/denodb/mod.ts';
const db = new Database('mongo', {
host: 'mongodb://localhost:27017',
username: '',
password: '',
database: 'DBMYAPP',
});
console.log(db)
I also faced the same issue while updating deno_mongo to the latest version. Use await to resolve client.connect method
Try this:
import { MongoClient } from "https://deno.land/x/mongo#v0.20.1/mod.ts";
const dbString = `mongodb://${mongoUser}:${mongoPass}#${mongoHost}:${mongoPort}`;
const client = new MongoClient();
await client.connect(dbString);
const db = client.database(mongoDB)
this.users = db.collection<UserSchema>("users");
I had the same problem on windows 10, so try this
on your local mongodb:
await client.connect("mongodb://127.0.0.1:27017");

How to set container port and load balancer for aws fargate using pulumi?

I am trying to deploy simple flask python app on aws fargate using Pulumi. The dockerfile of python app exposes port 8000 from container. How could I set it up with load balancer using pulumi?
I have tried the following so far, with index.ts (pulumi):
import * as awsx from "#pulumi/awsx";
// Step 1: Create an ECS Fargate cluster.
const cluster = new awsx.ecs.Cluster("first_cluster");
// Step 2: Define the Networking for our service.
const alb = new awsx.elasticloadbalancingv2.ApplicationLoadBalancer(
"net-lb", { external: true, securityGroups: cluster.securityGroups });
const web = alb.createListener("web", { port: 80, external: true });
// Step 3: Build and publish a Docker image to a private ECR registry.
const img = awsx.ecs.Image.fromPath("app-img", "./app");
// Step 4: Create a Fargate service task that can scale out.
const appService = new awsx.ecs.FargateService("app-svc", {
cluster,
taskDefinitionArgs: {
container: {
image: img,
cpu: 102 /*10% of 1024*/,
memory: 50 /*MB*/,
portMappings: [{ containerPort: 8000, }],
},
},
desiredCount: 5,
});
// Step 5: Export the Internet address for the service.
export const url = web.endpoint.hostname;
And when I curl the url curl http://$(pulumi stack output url), I get:
<html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body bgcolor="white">
<center><h1>503 Service Temporarily Unavailable</h1></center>
</body>
</html>
How could I map load balancer port to container port which is 8000?
You can specify the target port on the application load balancer:
const atg = alb.createTargetGroup(
"app-tg", { port: 8000, deregistrationDelay: 0 });
Then you can simply pass the listener to the service port mappings:
const appService = new awsx.ecs.FargateService("app-svc", {
// ...
taskDefinitionArgs: {
container: {
// ...
portMappings: [web],
},
},
});
Here is a full repro with a public docker container, so that anybody could start with a working sample:
import * as awsx from "#pulumi/awsx";
const cluster = new awsx.ecs.Cluster("first_cluster");
const alb = new awsx.elasticloadbalancingv2.ApplicationLoadBalancer(
"app-lb", { external: true, securityGroups: cluster.securityGroups });
const atg = alb.createTargetGroup(
"app-tg", { port: 8080, deregistrationDelay: 0 });
const web = atg.createListener("web", { port: 80 });
const appService = new awsx.ecs.FargateService("app-svc", {
cluster,
taskDefinitionArgs: {
container: {
image: "gcr.io/google-samples/kubernetes-bootcamp:v1",
portMappings: [web],
},
},
desiredCount: 1,
});
export const url = web.endpoint.hostname;