To solve this optimization problem, we first need to define the decision variables and the objective function. Let's denote the number of Caesar salads as $C$ and the number of house salads as $H$. The profit per Caesar salad is $5, and the profit per house salad is $8. Therefore, the total profit can be represented by the equation $5C + 8H$.

The constraints are:
1. **Lettuce Limitation**: Since there are only 2000 grams of lettuce available, and each Caesar salad contains 20 grams while each house salad contains 30 grams, we have the inequality $20C + 30H \leq 2000$.
2. **Popularity Constraint**: The problem states that at least three times the amount of house salads needs to be made compared to Caesar salads. This can be represented by $H \geq 3C$.
3. **Minimum Caesar Salads**: There must be a minimum of 8 Caesar salads made, which gives us $C \geq 8$.

All variables ($C$ and $H$) should be non-negative since we cannot make a negative number of salads.

The goal is to maximize the profit function $5C + 8H$ under these constraints.

Now, let's translate this problem into Gurobi code in Python:

```python
from gurobipy import *

# Create a model
m = Model("Salad_Optimization")

# Define decision variables
C = m.addVar(name='Caesar_Salads', vtype=GRB.INTEGER, lb=0)
H = m.addVar(name='House_Salads', vtype=GRB.INTEGER, lb=0)

# Objective function: Maximize profit
m.setObjective(5*C + 8*H, GRB.MAXIMIZE)

# Constraints
m.addConstr(20*C + 30*H <= 2000, name='Lettuce_Limitation')
m.addConstr(H >= 3*C, name='Popularity_Constraint')
m.addConstr(C >= 8, name='Minimum_Caesar_Salads')

# Solve the model
m.optimize()

# Print solution
if m.status == GRB.OPTIMAL:
    print(f"Optimal Solution: {C.varName} = {int(C.x)}")
    print(f"Optimal Solution: {H.varName} = {int(H.x)}")
else:
    print("Model is infeasible")

```