As I am aware, not may people know how to do even basic inverse kinematics. I would like that to change, so I have made this tutorial!
First of all, create a script and put it in the workspace.
Then create a module script and parent it to the script. You can cane the module script "create" since we will use it to create parts in our main script.
Inside of the module script put the following code:

return function(obj, prop)
	local object = Instance.new(obj)
	for i,v in pairs(prop) do
		object[i] = v
	end
	return object
end

This function will allow us to shorten our code, when obj is the type of object being created e.g "Part" and prop is a dictionary containing all of the properties we want to set for the new object (instance).

Then, in the main script we need to start writing code!
First of all we want to cache some important operations:

local cf, v3, ang, c3, sqrt, atan2 = CFrame.new, Vector3.new, CFrame.Angles, Color3.new, math.sqrt, math.atan2

This means we can write shorter code and do cf(0, 10, 0) instead of CFrame.new(0, 10, 0).
Next, we need to require our module:

local create = require(script.create)

This will allow us to use the function in our module.
Next we need to define the radius (or length) of our joints for the calculation:

local rad1, rad2 = 4, 4

NOTE : they both need to be the same length
Then we need create our start point and end point:

local startPoint = create("Part", 
	{["Name"] = "Start", ["Size"] = v3(0.5, 0.5, 0.5),["Anchored"] = true, ["CanCollide"] = false, 
	["Transparency"] = 0.8, ["CFrame"] = cf(0, 10, 0),["Parent"] = workspace})
local endPoint = create("Part", 
	{["Name"] = "End", ["Size"] = v3(0.5, 0.5, 0.5),["Anchored"] = true, ["CanCollide"] = false, 
	["Transparency"] = 0.8, ["CFrame"] = cf(0, 10, rad1 + rad2),["Parent"] = workspace})

This will create two parts, one at y of 10 and another at y of 10 and offset z of the two length of the joints added togther.
Next we need to create our joints:

local joint1 = create("Part", 
	{["Name"] = "Joint1", ["Size"] = v3(1, 1, rad1),["Anchored"] = true, ["CanCollide"] = false, 
	["CFrame"] = startPoint.CFrame * cf(0, 0, rad1/2), ["Color"] = c3(0.76, 0.15, 0.1),["Parent"] = workspace})
local joint2 = create("Part", 
	{["Name"] = "Joint2", ["Size"] = v3(1, 1, rad2),["Anchored"] = true, ["CanCollide"] = false, 
	["CFrame"] = startPoint.CFrame * cf(0, 0, rad1 + rad2/2), ["Color"] = c3(0.76, 0.15, 0.1),["Parent"] = workspace})

Then we need to create our function which calculates the joint:

local function calculateJoint(r1, r2, c, p)
	local dv = c:pointToObjectSpace(p)
	local dis = sqrt(dv.x^2 + dv.y^2 + dv.z^2)
	dis = r1 + r2 < dis and r1 + r2 or dis
end

All the function does so far is calculates the difference between the two points as a vector and as a number.
line 1:returns a vector 3 of the difference of x,y,z between the two points
line 2:returns a number which is total difference in studs between the two points which is defined by Pythagoras theorem: a^2 + b^2 + c^2 = d^2.
line 3: makes it so that distance is withing range.
Our next line in our function is:

local l = (r2^2 - r1^2 - dis^2)/(2*r1*dis)

which will always return a number between 0 and -1
Then our next line is:

local h = sqrt(1 - l^2)

which will always be between 1 and 1.414 since it will do for example sqrt(1 - - 0.5^2) = sqrt(1 + 0.25) = sqrt(1.25) = 1.118
and then we do:

local a = atan2(-h, -l)

Which will return the arc*tangent of y/x and uses the signs of both parameters to find the quadrant of the result (you don't have to know what that means)
And then we calculate where the two joints will go with:

local c0 = c * cf(v3(), dv) * ang(a, 0, 0)
local c1 = c0 * cf(0, 0, -r1) * ang(-2*a, 0, 0)

return c0, c1

So our whole function is:

local function calculateJoint(r1, r2, c, p)
	local dv = c:pointToObjectSpace(p)
	local dx, dy, dz = dv.x, dv.y, dv.z
	local dis = sqrt(dx^2 + dy^2 + dz^2)
	dis = r1 + r2 < dis and r1 + r2 or dis
	
	local l = (r2^2 - r1^2 - dis^2)/(2*r1*dis)
	local h = sqrt(1 - l^2)
	local a = atan2(-h, -l)
	
	local c0 = c * cf(v3(), dv) * ang(a, 0, 0)
	local c1 = c0 * cf(0, 0, -r1) * ang(-2*a, 0, 0)
	
	return c0 * cf(0, 0, -r1/2), c1  * cf(0, 0, -r2/2)
end

Then we need to update it each render step:

game:GetService("RunService").RenderStepped:Connect(function()
	local c, p = startPoint.CFrame, endPoint.Position
	local c0, c1 = calculateJoint(rad1, rad2, c, p)
	joint1.CFrame = c0
	joint2.CFrame = c1
end)

which is all of our code!
(I'm sorry near the end I didn't want to explain things, It was taking long time)
Here's our whole code:
Module script:

return function(obj, prop)
	local object = Instance.new(obj)
	for i,v in pairs(prop) do
		object[i] = v
	end
	return object
end

Script:

local cf, v3, ang, c3, sqrt, atan2 = CFrame.new, Vector3.new, CFrame.Angles, Color3.new, math.sqrt, math.atan2
local create = require(script.create)

local rad1, rad2 = 4, 4

local startPoint = create("Part", 
	{["Name"] = "Start", ["Size"] = v3(0.5, 0.5, 0.5),["Anchored"] = true, ["CanCollide"] = false, 
	["Transparency"] = 0.8, ["CFrame"] = cf(0, 10, 0),["Parent"] = workspace})
local endPoint = create("Part", 
	{["Name"] = "End", ["Size"] = v3(0.5, 0.5, 0.5),["Anchored"] = true, ["CanCollide"] = false, 
	["Transparency"] = 0.8, ["CFrame"] = cf(0, 10, rad1 + rad2),["Parent"] = workspace})

local joint1 = create("Part", 
	{["Name"] = "Joint1", ["Size"] = v3(1, 1, rad1),["Anchored"] = true, ["CanCollide"] = false, 
	["CFrame"] = startPoint.CFrame * cf(0, 0, rad1/2), ["Color"] = c3(0.76, 0.15, 0.1),["Parent"] = workspace})
local joint2 = create("Part", 
	{["Name"] = "Joint2", ["Size"] = v3(1, 1, rad2),["Anchored"] = true, ["CanCollide"] = false, 
	["CFrame"] = startPoint.CFrame * cf(0, 0, rad1 + rad2/2), ["Color"] = c3(0.76, 0.15, 0.1),["Parent"] = workspace})

local function calculateJoint(r1, r2, c, p)
	local dv = c:pointToObjectSpace(p)
	local dx, dy, dz = dv.x, dv.y, dv.z
	local dis = sqrt(dx^2 + dy^2 + dz^2)
	dis = r1 + r2 < dis and r1 + r2 or dis
	
	local l = (r2^2 - r1^2 - dis^2)/(2*r1*dis)
	local h = sqrt(1 - l^2)
	local a = atan2(-h, -l)
	
	local c0 = c * cf(v3(), dv) * ang(a, 0, 0)
	local c1 = c0 * cf(0, 0, -r1) * ang(-2*a, 0, 0)
	
	return c0 * cf(0, 0, -r1/2), c1  * cf(0, 0, -r2/2)
end

game:GetService("RunService").RenderStepped:Connect(function()
	local c, p = startPoint.CFrame, endPoint.Position
	local c0, c1 = calculateJoint(rad1, rad2, c, p)
	joint1.CFrame = c0
	joint2.CFrame = c1
end)

I hope this helped!